- Để có thể biết
select
như thế nào, cách sử dụng, hãy xem ví dụ dưới.
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">func</span> <span class="token function">example</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> ch1<span class="token punctuation">,</span> ch2 <span class="token operator"><-</span><span class="token keyword">chan</span> <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">var</span> ch3 <span class="token keyword">chan</span><span class="token operator"><-</span> <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">select</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token operator"><-</span>ch1<span class="token punctuation">:</span> <span class="token comment">// Do something</span> <span class="token keyword">case</span> <span class="token operator"><-</span>ch2<span class="token punctuation">:</span> <span class="token comment">// Do something</span> <span class="token keyword">case</span> ch3 <span class="token operator"><-</span> <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">:</span> <span class="token comment">// Do something</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- nhìn code thì nó gần như giống với
switch case
, nhưng thực tế là không, nó là list của các case statement, và là điểm kết thúc củaselect
là cáccase
. - Không như
switch case
các case không thực hiện tuần tự và cũng sẽ không tự động thực thi nếu không có tín hiệu đầu vào, ở đây là 1channel
- Thay vào đó, tất cả
channel
sẽ được đọc và ghi đồng thời, nếu trong trường hợp không cóchannel
nào ready, thì tất cả sẽ block. Đợi đến khi có 1 channel sắn sàng, thì sẽ lựa chọn case tương ứng để thực thi. - ví dụ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">func</span> <span class="token function">Example2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> start <span class="token operator">:=</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span> c <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">5</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span> <span class="token function">close</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token comment">// (1)</span> <span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Blocking on read..."</span><span class="token punctuation">)</span> <span class="token keyword">select</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token operator"><-</span>c<span class="token punctuation">:</span> <span class="token comment">// (2)</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"Unblocked %v later.n"</span><span class="token punctuation">,</span> time<span class="token punctuation">.</span><span class="token function">Since</span><span class="token punctuation">(</span>start<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- ghi chú:
- (1) close
channel
sau khi đợi 5 giây. - (2) đợi để read value từ
channel
.
- (1) close
- output:
1 2 3 | Blocking on read... Unblocked 5.001199292s later. |
- như kết quả thì process sẽ unblock sau 5 giây.
- trong trường hợp này, cũng là một cách đơn giản và hiệu quả để đợi một process đang xảy ra cho đến khi kết thúc. Nhưng sẽ có một số vấn đặt ra
- Điều gì sẽ xảy ra khi nhiều
channel
cùng read. - Điều gì sẽ xảy ra khi không có
channel
ready. - Sẽ như thế nào nếu bạn muốn process 1 logic khác mà không có
channel
ready.
- Điều gì sẽ xảy ra khi nhiều
- Cùng đi giải quyết từng vấn đề
- nhiều channel cùng read:
- code example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token keyword">func</span> <span class="token function">MulChannel</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> ch1 <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token function">close</span><span class="token punctuation">(</span>ch1<span class="token punctuation">)</span> ch2 <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token function">close</span><span class="token punctuation">(</span>ch2<span class="token punctuation">)</span> <span class="token keyword">var</span> ch1Count<span class="token punctuation">,</span> ch2Count <span class="token builtin">int</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">1000</span><span class="token punctuation">;</span> i <span class="token operator">>=</span> <span class="token number">0</span><span class="token punctuation">;</span> i<span class="token operator">--</span> <span class="token punctuation">{</span> <span class="token keyword">select</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token operator"><-</span>ch1<span class="token punctuation">:</span> ch1Count<span class="token operator">++</span> <span class="token keyword">case</span> <span class="token operator"><-</span>ch2<span class="token punctuation">:</span> ch2Count<span class="token operator">++</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"ch1Count: %dn ch2Count: %dn"</span><span class="token punctuation">,</span> ch1Count<span class="token punctuation">,</span> ch2Count<span class="token punctuation">)</span> <span class="token punctuation">}</span> |
- output:
1 2 3 4 5 6 7 8 | - lan1 ch1Count: 498 ch2Count: 503 - lan2 ch1Count: 479 ch2Count: 522 |
- Như kết quả, thì sau vòng lặp sấp xỉ một nữa được read bởi
ch1
và một nữach2
. Có vẻ ngẫu nhiên nhỉ, vì trongGolang
các case sẽ random giữa các statement nếu như cóchannel
ready. Vậy trong một chương trình thực tế không thể để các state như thế được.
- Không có channel là ready.
- Điều gì sẽ xảy ra khi không có channel nào ready, trong thực thế thì không thể để select block
forever
, do đó cần có timeout. Trong golang có package time có thể xử lý timeout được. - code:
1 2 3 4 5 6 7 | <span class="token keyword">var</span> ch <span class="token operator"><-</span><span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token keyword">select</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token operator"><-</span>ch<span class="token punctuation">:</span> <span class="token comment">// (1) sẽ không bao giờ unblock bởi vì read từ 1 channel nill</span> <span class="token keyword">case</span> <span class="token operator"><-</span>time<span class="token punctuation">.</span><span class="token function">After</span><span class="token punctuation">(</span><span class="token number">2</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span><span class="token punctuation">:</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Timed out."</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
- output:
1 2 | Timed out. |
- điều gì xảy ra khi không có channel nào ready, và cần làm một process khác sau 1 thời gian.
- như
switch-case
thìselect-case
cũng có giá trịdefault
default
là cho bạn lựa chọn khi tất cả các select case còn lại luôn luônblock
- code example 1:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">func</span> <span class="token function">SelectDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> start <span class="token operator">:=</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">var</span> c1<span class="token punctuation">,</span> c2 <span class="token operator"><-</span><span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token keyword">select</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token operator"><-</span>c1<span class="token punctuation">:</span> <span class="token keyword">case</span> <span class="token operator"><-</span>c2<span class="token punctuation">:</span> <span class="token keyword">default</span><span class="token punctuation">:</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"Value default %vnn"</span><span class="token punctuation">,</span> time<span class="token punctuation">.</span><span class="token function">Since</span><span class="token punctuation">(</span>start<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- output:
1 2 3 4 | Value default 334ns -- Value default 208ns |
- Như kết quả thì
default
sẽ giúp ta ngăn chặn block - Cũng có thể được xem là waiting để đợi một
goroutine
khác process, và nhận kết quả - code-example-2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token keyword">func</span> <span class="token function">WaitingOther</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> done <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">3</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"done"</span><span class="token punctuation">)</span> <span class="token function">close</span><span class="token punctuation">(</span>done<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span> workCounter <span class="token operator">:=</span> <span class="token number">0</span> loop<span class="token punctuation">:</span> <span class="token keyword">for</span> <span class="token punctuation">{</span> <span class="token keyword">select</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token operator"><-</span>done<span class="token punctuation">:</span> <span class="token keyword">break</span> loop <span class="token keyword">default</span><span class="token punctuation">:</span> <span class="token punctuation">}</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>workCounter<span class="token punctuation">)</span> workCounter<span class="token operator">++</span> time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span> <span class="token punctuation">}</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"workCounter %v .n"</span><span class="token punctuation">,</span> workCounter<span class="token punctuation">)</span> <span class="token punctuation">}</span> |
- output:
1 2 3 4 5 6 | 0 1 2 done workCounter 3 |
- Như ví dụ, thì chúng ta có 1 vòng lặp sẽ đợi 1 goroutine khác process, trong ví dụ thì process đó là
sleep 3s
Nguồn: book golang concurrency