Skip to content

Commit

Permalink
update chap02
Browse files Browse the repository at this point in the history
  • Loading branch information
phamtai97 committed Aug 10, 2019
2 parents 7d8432e + 37fdb3e commit d014f4a
Show file tree
Hide file tree
Showing 86 changed files with 311,598 additions and 973 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea
.vscode
.vscode
.txt
13 changes: 10 additions & 3 deletions ch1-basic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@
</div>
<br/>

>*Tôi không biết tại sao bạn lại không vui trong suốt 10 năm qua. Nhưng hãy tin tôi đi, xóa bỏ đi những nặng nhọc trong quá khứ, dùng ngôn ngữ Go và trải nghiệm niềm hạnh phúc bắt đầu!*
>*Những viên gạch nhỏ được đóng góp cuối cùng sẽ xây được một lâu đài kiên cố - Xiao Zhang*
>*“Go is no Erlang, Smalltalk or Scheme, nothing pure. But it works great and is fun!” – Frank Mueller (@themue)"*
Chương này bắt đầu bằng vài lời giới thiệu về lịch sử của ngôn ngữ Go và phân tích chi tiết cuộc cách mạng của chương trình "Hello World" với những thế hệ ngôn ngữ đi trước. Sau đó, một số cấu trúc dữ liệu sẽ được trình bày như `arrays`, `strings`, và `slices`, tính chất `process-oriented``duck typing` được thể hiện qua `functions`, `methods`, và `interfaces`, đặc biệt là mô hình `concurrent programming``error handling` cũng được giới thiệu sơ qua. Cuối cùng, một số trọng tâm trong việc phát triển chương trình trên các nền tảng macOS, Windows, và Linux, cũng như một vài editor và môi trường phát triển tích hợp (IDE) cũng được đề cập, bởi vì có công cụ tốt thì năng suất làm việc mới tăng lên.

Quyển sách này được xác định là một trong những quyển sách nâng cao về Golang, vì vậy người đọc cần có một nền tảng Golang nhất định. Nếu bạn không biết nhiều về Golang, tác giả khuyên bạn nên học Golang với một số gợi ý sau.

- Sau khi cài đặt Golang và tải hướng dẫn bằng `go get golang.org/x/tour`, có thể xem hướng dẫn [A Tour of Go](https://tour.golang.org/welcome/1) ngay ở local bằng lệnh `tour`.
- Bạn cũng có thể đọc hướng dẫn ["Ngôn ngữ lập trình Go"](http://www.gopl.io/) được xuất bản bởi team Golang chính thức . ["Ngôn ngữ lập trình Go"](http://www.gopl.io/) được gọi là *Kinh thánh* Golang trong cộng đồng Golang mà bạn phải đọc thật bài bản.

Trong khi học, hãy cố gắng giải quyết một số vấn đề nhỏ với Golang. Nếu bạn muốn tham khảo API, có thể mở truy vấn tài liệu tích hợp bằng lệnh `godoc`.

Từ thời điểm này chúng ta sẽ mặc định rằng bạn có thể sử dụng Golang một cách thuần thục.
10 changes: 5 additions & 5 deletions ch1-basic/ch1-01-genesis.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Ngôn ngữ [Go](https://golang.org/) ban đầu được thiết kế và phát triển bởi một nhóm kĩ sư Google bao gồm **Robert Griesemer**, **Ken Thompson****Rob Pike** vào năm 2007. Mục đích của việc thiết kế ngôn ngữ mới bắt nguồn từ một số phản hồi về tính chất phức tạp của C++11 và nhu cầu thiết kế lại ngôn ngữ C trong môi trường network và multi-core.

Vào giữa năm 2008, hầu hết các tính năng được thiết kế trong ngôn ngữ được hoàn thành, họ bắt đầu hiện thực trình biên dịch (compiler) và bộ thực thi (runtime) với **Russ Cox** là nhà phát triển chính. Trước năm 2010, ngôn ngữ Go dần dần được hoàn thiện. Vào tháng 9 cùng năm, ngôn ngữ Go chính thức được công bố dưới dạng Open source.
Vào giữa năm 2008, hầu hết các tính năng được thiết kế trong ngôn ngữ được hoàn thành, họ bắt đầu hiện thực trình biên dịch (compiler) và Go runtime với **Russ Cox** là nhà phát triển chính. Trước năm 2010, ngôn ngữ Go dần dần được hoàn thiện. Vào tháng 9 cùng năm, ngôn ngữ Go chính thức được công bố dưới dạng Open source.

Ngôn ngữ Go thường được mô tả là "Ngôn ngữ tựa C" hoặc là "Ngôn ngữ C của thế kỉ 21". Từ nhiều khía cạnh, ngôn ngữ Go thừa hưởng những ý tưởng từ ngôn ngữ C, như là cú pháp, cấu trúc điều khiển, kiểu dữ liệu cơ bản, thủ tục gọi, trả về, con trỏ, v,v.., hoàn toàn kế thừa và phát triển ngôn ngữ C, hình bên dưới mô tả sự liên quan của ngôn ngữ Go với các ngôn ngữ khác.

Expand Down Expand Up @@ -33,9 +33,9 @@ Một vài những tính năng khác của ngôn ngữ Go đến từ một số

Tính chất concurrency của Go đến từ học thuyết [Commutative sequential processes (CSP)](https://www.cs.cmu.edu/~crary/819-f09/Hoare78.pdf) được công bố bởi Tony Hoare tại Bell Labs vào năm 1978. Bài báo khoa học về CSP nói rằng chương trình chỉ là một tập hợp các tiến trình được chạy song song, mà không có sự chia sẻ về trạng thái, sử dụng `channel` cho việc giao tiếp và điều khiển đồng bộ.

Học thuyết CSP của Tony Hoare chỉ là một mô hình lập trình với những khái niệm cơ bản về concurrency (tính đồng thời), nó cũng không hẳn là một ngôn ngữ lập trình. Qua việc thiết kế Go, Rob Pike đã tổng hợp nhiều thập kỉ trong việc ứng dụng học thuyết CSP trong việc xây dựng ngôn ngữ lập trình.
Học thuyết CSP của Tony Hoare chỉ là một mô hình lập trình với những khái niệm cơ bản về concurrency (tính đồng thời), nó cũng không hẳn là một ngôn ngữ lập trình. Qua việc thiết kế Go, Rob Pike đã tổng hợp nhiều thập kỷ trong việc ứng dụng học thuyết CSP trong việc xây dựng ngôn ngữ lập trình.

Ngôn ngữ [Erlang](https://en.wikipedia.org/wiki/Erlang_(programming_language)) là một hiện thực khác của học thuyết **CSP**.
Ngôn ngữ **Erlang** là một hiện thực khác của học thuyết **CSP**, bạn có thể tìm kiếm thông tin về ngôn ngữ này trên [trang chủ Erlang](https://www.erlang.org/).

Hình dưới chỉ ra lịch sử phát triển của ngôn ngữ Go qua codebase logs.

Expand All @@ -59,7 +59,7 @@ Có thể nhìn thấy từ những submission log rằng ngôn ngữ Go đượ
</div>
<br/>

Trong vòng những năm gần đây, Go là một ngôn ngữ được ưa chuộng khi viết các chương trình có kiến trúc Micro Services, vì những đặc tính nhỏ gọn, biên dịch nhanh, import thư viện từ github, cú pháp đơn giản nhưng hiện đại.
Trong vòng những năm gần đây, Go là một ngôn ngữ được ưa chuộng khi viết các chương trình Micro Services, vì những đặc tính nhỏ gọn, biên dịch nhanh, import thư viện từ github, cú pháp đơn giản nhưng hiện đại.

## 1.1.2. Hello World

Expand All @@ -77,7 +77,7 @@ import "fmt"
// main là hàm đầu tiên được chạy
func main() {

// in ra dòng chữ "Hello World"
// in ra dòng chữ "Hello World"
fmt.Println("Hello World")
}
```
Expand Down
2 changes: 1 addition & 1 deletion ch1-basic/ch1-02-hello-revolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ Ngữ pháp của Alef về cơ bản giống như ngôn ngữ C. Nó có thể
## 1.2.5. Limbo - Sean Dorward, Phil Winterbottom, Rob Pike, 1995
Limbo (Hell) là ngôn ngữ lập trình để phát triển các ứng dụng phân tán chạy trên máy tính nhỏ. Nó hỗ trợ lập trình mô-đun, kiểm tra kiểu mạnh vào thời gian biên dịch và thời gian chạy, liên lạc bên trong process thông qua đường ống (pipeline), có bộ thu gom rác tự động. Có các loại dữ liệu trừu tượng đơn giản. Limbo được thiết kế để hoạt động an toàn ngay cả trên các thiết bị nhỏ mà không cần bảo vệ bộ nhớ phần cứng. Ngôn ngữ Limbo chạy chủ yếu trên hệ thống Inferno.
Limbo (Hell) là ngôn ngữ lập trình để phát triển các ứng dụng phân tán chạy trên máy tính nhỏ. Nó hỗ trợ lập trình mô-đun, kiểm tra kiểu mạnh vào thời gian biên dịch và thời gian chạy, liên lạc bên trong process thông qua channel, có bộ thu gom rác tự động. Có các loại dữ liệu trừu tượng đơn giản. Limbo được thiết kế để hoạt động an toàn ngay cả trên các thiết bị nhỏ mà không cần bảo vệ bộ nhớ phần cứng. Ngôn ngữ Limbo chạy chủ yếu trên hệ thống Inferno.
Phiên bản Limbo của chương trình "Hello World" như sau:
Expand Down
10 changes: 5 additions & 5 deletions ch1-basic/ch1-03-array-string-and-slice.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ var decoder2 = [...]func(io.Reader) (image.Image, error){
var unknown1 [2]interface{}
var unknown2 = [...]interface{}{123, "Hello!"}

// Mảng pipe
// Mảng channel
var chanList = [2]chan int{}
```

Expand All @@ -142,16 +142,16 @@ var f = [...]int{}

Một array có chiều dài 0 thì không chiếm không gian lưu trữ.

## 1.3.2 String
## 1.3.2. String

<div align="center">
<img src="../images/1-3-string.jpeg" width="600">
<img src="../images/ch1-string.png" width="400">
</div>
<br/>

`string` cũng là một array của các `byte` dữ liệu, nhưng khác với array những phần tử của string là [immutable](https://en.wikipedia.org/wiki/Immutable_object)

Cấu trúc `reflect.StringHeader` được định nghĩa với:
Cấu trúc [reflect.StringHeader](https://golang.org/src/reflect/value.go?s=56526:56578#L1873) được dùng để biểu diễn string :

```go
type StringHeader struct {
Expand Down Expand Up @@ -197,7 +197,7 @@ s1 := "hello world"[:5]
s2 := "hello world"[6:]
```

Tương tự như array, String cũng có một hàm build-in là `len` dùng để trả về chiều dài của string, ngoài ra bạn có thể dùng `reflect.StringHeader` để truy xuất chiều dài của string theo cách như sau
Tương tự như array, String cũng có một hàm built-in là `len` dùng để trả về chiều dài của string, ngoài ra bạn có thể dùng `reflect.StringHeader` để truy xuất chiều dài của string theo cách như sau

```go
fmt.Println("len(s): ", (*reflect.StringHeader)(unsafe.Pointer(&s)).Len)
Expand Down
22 changes: 19 additions & 3 deletions ch1-basic/ch1-04-func-method-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ Ta thường sử dụng `defer` cho việc đóng hoặc giải phóng tài ngu
if err != nil {
panic("cannot create file")
}

// chắc chắn file sẽ được close dù hàm có bị panic hay return
defer f.Close()
// no matter what happens here file will be closed
// for sake of simplicity I skip checking close result
fmt.Fprintf(f,"hello")
}
```
Expand Down Expand Up @@ -135,6 +135,15 @@ Ta thường sử dụng `defer` cho việc đóng hoặc giải phóng tài ngu

### Slice trong Function

<div align="center">
<img src="../images/slice_1.png" width="300">
<br/>
<span align="center">
<i>Minh hoạ slice</i>
</span>
</div>
<br/>

Mọi thứ trong Go đều được truyền theo kiểu pass by value, slice cũng thế. Nhưng vì giá trị của slice là một *header* (chứa con trỏ tới dữ liệu array bên dưới) nên khi truyền slice vào hàm, quá trình copy sẽ bao gồm luôn địa chỉ tới array chứa dữ liệu thực sự.

Ví dụ sau cho thấy ý nghĩa của việc truyền tham số kiểu slice vào hàm thay vì array:
Expand Down Expand Up @@ -263,7 +272,6 @@ func main() {
Trong một số tình huống, ta quan tâm nhiều hơn đến một chuỗi thao tác ví dụ như `Read` đọc một số mảng và sau đó gọi `Close` để đóng, trong ngữ cảnh này, người dùng không quan tâm đến kiểu của đối tượng, miễn là nó có thể đáp ứng được các thao tác của `Read``Close`. Tuy nhiên trong các biểu thức phương thức của `ReadFile`, `CloseFile` có chỉ rõ kiểu `File` trong tham số kiểu sẽ khiến chúng không bị phụ thuộc vào đối tượng nào cụ thể. Việc này có thể khắc phục bằng cách sử dụng thuộc tính closure (closure property):

```go
func main() {
var data []byte
Expand Down Expand Up @@ -400,6 +408,14 @@ Duck-typing với ý tưởng đơn giản:

> If something looks like a duck, swims like a duck and quacks like a duck then it’s probably a duck.

<div align="center">
<img src="../images/duck-typing.png" width="300">
<br/>
<span align="center">
</span>
</div>
<br/>

Ví dụ có một interface con vịt, xác định khả năng `Quacks`:

```go
Expand Down
48 changes: 17 additions & 31 deletions ch1-basic/ch1-05-mem.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@

Thời gian đầu, CPU chỉ có một lõi duy nhất, các ngôn ngữ khi đó sẽ theo mô hình lập trình tuần tự, điển hình là ngôn ngữ C. Ngày nay, với sự phát triển của công nghệ đa xử lý, để tận dụng tối đa sức mạnh của CPU, mô hình lập trình song song hay [multi-threading](https://en.wikipedia.org/wiki/Multithreading_(computer_architecture)) thường thấy trên các ngôn ngữ lập trình ra đời. Ngôn ngữ Go cũng phát triển mô hình lập trình song song rất hiệu quả với khái niệm Goroutines.

<div align="center" width="600">
<img src="../images/concurency.jpg">
<br/>
<span align="center"><i>Quá trình khởi tạo package</i></span>
</div>
<br/>

Lập trình tuần tự|Lập trình song song
---|---
![](../images/ch1-5-sequence-programming.png) | ![](../images/ch1-5-parallelprograming.png)

## 1.5.1. Goroutines và system threads

Expand All @@ -17,15 +14,19 @@ Goroutines là một đơn vị concurrency của ngôn ngữ Go. Việc khởi
Đầu tiên, system thread sẽ có một kích thước vùng nhớ stack cố định (thông thường vào khoảng 2MB). Vùng nhớ stack chủ yếu được dùng để lưu trữ những tham số, biến cục bộ và địa chỉ trả về khi chúng ta gọi hàm.

Kích thước cố định của stack sẽ dẫn đến hai vấn đề:
* Lãng phí vùng nhớ đối với chương trình đơn giản
* StackOverflow với những chương trình gọi hàm phức tạp.
* Stack overflow với những chương trình gọi hàm đệ quy sâu.
* Lãng phí vùng nhớ đối với chương trình đơn giản.


Giải pháp cho vấn đề này chính là cấp phát linh hoạt vùng nhớ stack:
* Một Goroutines sẽ được bắt đầu bằng một vùng nhớ nhỏ (khoảng 2KB hoặc 4KB).
* Khi gọi đệ quy sâu (không gian stack hiện tại là không đủ) Goroutines sẽ tự động tăng không gian stack (kích thước tối đa của stack có thể được đạt tới 1GB)
* Bởi vì chi phí của việc khởi tạo là nhỏ, chúng ta có thể dễ dàng giải phóng hàng ngàn goroutines.

Bộ thực thi (runtime) Go có riêng cơ chế định thời cho Goroutines, nó dùng một số kỹ thuật để ghép M Goroutines trên N thread của hệ thống. Cơ chế định thời Goroutines tương tự với cơ chế định thời của `kernel` nhưng chỉ ở mức chương trình. Biến `runtime.GOMAXPROCS` quy định số lượng system thread hiện thời chạy trên các Goroutines.
Go runtime có riêng cơ chế định thời cho Goroutines, nó dùng một số kỹ thuật để ghép M Goroutines trên N thread của hệ thống. Cơ chế định thời Goroutines tương tự với cơ chế định thời của `kernel` nhưng chỉ ở mức chương trình. Biến `runtime.GOMAXPROCS` quy định số lượng system thread hiện thời chạy trên các Goroutines.

> https://yourbasic.org/golang/goroutines-explained/
> https://medium.com/rungo/achieving-concurrency-in-go-3f84cbf870ca
## 1.5.2. Tác vụ Atomic

Expand Down Expand Up @@ -195,8 +196,9 @@ func Instance() *singleton {
}
```

* Package `sync/atomic` sẽ hỗ trợ những tác vụ atomic cho những kiểu cơ bản.
* Cho việc đọc và ghi một đối tượng phức tạp, `atomic.Value` sẽ hỗ trợ hai hàm `Load``Store` để load và save dữ liệu, trả về giá trị và tham số là `interface{}` nó có thể được sử dụng trong một vài kiểu đặc biệt.
Package `sync/atomic` sẽ hỗ trợ những tác vụ atomic cho những kiểu cơ bản.

Cho việc đọc và ghi một đối tượng phức tạp, `atomic.Value` sẽ hỗ trợ hai hàm `Load``Store` để load và save dữ liệu, trả về giá trị và tham số là `interface{}` nó có thể được sử dụng trong một vài kiểu đặc biệt.

```go
var config atomic.Value
Expand Down Expand Up @@ -254,23 +256,7 @@ func main() { // Goroutine (1)
}
```

## 1.5.4 Chuỗi khởi tạo

* Việc khởi tạo và thực thi chương trình Go luôn luôn bắt đầu bằng hàm `main.main`.
* Tuy nhiên nếu package `main` import các package khác vào, chúng sẽ được import theo thứ tự string của tên file và tên thư mục.
* Nếu một package được import nhiều lần, thì những lần import sau được bỏ qua.
* Nếu các package lại import các package khác nữa, chúng sẽ import vào khởi tạo các biến theo chiều sâu như hình bên dưới:

<div align="center" width="600">
<img src="../images/ch1-12-init.ditaa.png">
<br/>
<span align="center"><i>Quá trình khởi tạo package</i></span>
</div>
<br/>

* Nếu hàm `init` giải phóng một Goroutine mới với từ khóa `go`, thì Goroutine và `main.main` sẽ được thực thi một cách tuần tự. Bởi vì tất cả hàm `init` và hàm `main` sẽ được hoàn thành trong cùng một thread, nó cũng sẽ thoả mãn thứ tự về mô hình nhất quán.

## 1.5.5 Khởi tạo một Goroutine
## 1.5.4. Khởi tạo một Goroutine

Mệnh đề đứng sau từ khóa `go` sẽ tạo ra một Goroutine mới, ví dụ :

Expand All @@ -289,9 +275,9 @@ func hello() {
}
```

## 1.5.6 Giao tiếp thông qua kênh Channel
## 1.5.5. Giao tiếp thông qua kênh Channel

* Tác vụ gửi trên một unbufferred Channel luôn luôn xảy ra trước tác vụ nhận, dùng tính chất này cho việc đồng bộ giữa các goroutines.
Tác vụ gửi trên một unbufferred Channel luôn luôn xảy ra trước tác vụ nhận, dùng tính chất này cho việc đồng bộ giữa các goroutines.

```go
// dùng channel done để đồng bộ thứ tự thực thi
Expand Down Expand Up @@ -341,7 +327,7 @@ func main() {

Dòng `select{}` cuối cùng là một mệnh đề lựa chọn một empty channel sẽ làm cho main thread bị block, ngăn chặn chương trình kết thúc sớm. Tương tự `for{}``<- make(chan int)` nhiều hàm khác sẽ đạt được kết quả tương tự.

## 1.5.7 Tác vụ đồng bộ không tin cậy
## 1.5.6. Tác vụ đồng bộ không tin cậy

Như chúng ta phân tích trước, đoạn code sau sẽ không đảm bảo thứ tự in ra kết quả bình thường. Việc chạy thực sự bên dưới sẽ có một xác suất lớn kết quả sẽ không bình thường.

Expand Down
Loading

0 comments on commit d014f4a

Please sign in to comment.