西西軟件園多重安全檢測(cè)下載網(wǎng)站、值得信賴的軟件下載站!
軟件
軟件
文章
搜索

首頁(yè)編程開發(fā)C#.NET → 用 C# 實(shí)現(xiàn)Google Go 語(yǔ)言中的Channel 和 goroutine 機(jī)制

用 C# 實(shí)現(xiàn)Google Go 語(yǔ)言中的Channel 和 goroutine 機(jī)制

相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來源:本站整理時(shí)間:2010/8/25 19:35:49字體大。A-A+

作者:佚名點(diǎn)擊:110次評(píng)論:1次標(biāo)簽: Google Channel

3D建模(Google SketchUp)8.0.11752 官方免費(fèi)版
  • 類型:3D 制作大。35.7M語(yǔ)言:英文 評(píng)分:4.1
  • 標(biāo)簽:
立即下載

前段時(shí)間嘗試了一點(diǎn) Google 的 Go 語(yǔ)言,感覺其很多特性還是不錯(cuò)的。Go 語(yǔ)言旨在結(jié)合傳統(tǒng)編譯型的靜態(tài)語(yǔ)言和解釋型的動(dòng)態(tài)語(yǔ)言的優(yōu)點(diǎn),在其中找到一個(gè)平衡。從而打造一個(gè)既快速(編譯執(zhí)行),又方便編程的語(yǔ)言(動(dòng)態(tài)語(yǔ)言往往語(yǔ)法簡(jiǎn)單快捷)。同時(shí),Go 語(yǔ)言還具備豐富的特性以支持并發(fā)編程,這在現(xiàn)在多核非常普及的情況下,是很重要和強(qiáng)大的一個(gè)功能。

Go 語(yǔ)言的并發(fā)特性主要有 goroutine, channel 等。goroutine - 可以大致理解為一種輕量級(jí)的線程(或微線程),它是一種“分配在同一個(gè)地址空間內(nèi)的,能夠并行執(zhí)行的函數(shù)”。同時(shí),它是輕量級(jí)的,不需要像分配線程那樣分配獨(dú)立的?臻g。所以理論上講,我們可以很容易的分配很多個(gè) goroutine, 讓它們并發(fā)執(zhí)行,而其開銷則比多線程程序要小得多,從而可以讓程序支持比較大的并發(fā)性。

channel - 顧名思義,就是通道。通道的目的是用來傳遞數(shù)據(jù)。在一個(gè)通道上我們可以執(zhí)行數(shù)據(jù)的發(fā)送(Send)和接受(Receive)操作。對(duì)于非緩沖的 channel 而言,Receive 方法執(zhí)行時(shí),會(huì)判斷該通道上是否有值,如果沒有就會(huì)等待(阻塞),直到有一個(gè)值為止。同樣,在 channel 上有值,而尚未被一個(gè) Receiver 接受的時(shí)候,Send 方法也會(huì)阻塞,直到 Channel 變空。這樣,通過一個(gè)簡(jiǎn)單的機(jī)制就可以保證 Send 和 Receive 總是在不同的時(shí)間執(zhí)行的,而且只有 Send 之后才能 Receive. 這樣就避免了常規(guī)的多線程編程中數(shù)據(jù)共享的問題。正如 Go 語(yǔ)言的文檔一句話所說:

Do not communicate by sharing memory; instead, share memory by communicating.

不要通過共享內(nèi)存來溝通;而是通過溝通來共享內(nèi)存。

在常規(guī)的多線程編程里,我們總是定義好一些類變量,如果這些變量有可能被多個(gè)線程同時(shí)訪問,那么就需要加鎖。這樣帶來了一定的編程復(fù)雜性,如果代碼寫的稍有bug,則會(huì)導(dǎo)致讀/寫到錯(cuò)誤的值。

而通過 channel 來溝通,我們得到了一個(gè)更為清晰的溝通方式。兩個(gè)線程(或者 goroutine)要讀寫相同的數(shù)據(jù),則創(chuàng)建一個(gè)通道,雙方通過對(duì)這個(gè)通道執(zhí)行 Send / Receive 的操作來設(shè)值或取值即可,相對(duì)而言,比較不容易出錯(cuò)。

為了更好的理解這個(gè)原理,我嘗試了在 C# 中實(shí)現(xiàn)類似的功能。

相對(duì)于 goroutine, 我沒有去實(shí)現(xiàn)微線程,因?yàn)檫@需要更復(fù)雜的調(diào)度機(jī)制(打算接下來進(jìn)一步研究這方面)。我們可以暫時(shí)利用 Thread 來簡(jiǎn)單的模擬之。

而 Channel, 則用 Semaphone 控制同步的 Send / Receive 就可以了。

首先讓我們來實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 Channel,思想上面已經(jīng)說過了:

view sourceprint?01 /// <summary>

02 /// 先實(shí)現(xiàn)簡(jiǎn)單的沒有緩沖的 Channel.

03 /// </summary>

04 /// <typeparam name="T"></typeparam>

05 public class Channel<T>

06 {

07 T _value;

08

09 // 開始不能 Receive.

10 Semaphore _canReceive = new Semaphore(0, 1);

11

12 // 開始沒有值,可以 Send

13 Semaphore _canSend = new Semaphore(1, 1);

14

15 public T Receive()

16 {

17 // 等待有值

18 _canReceive.WaitOne();

19

20 T value = _value;

21

22 // 通知可以發(fā)送新的值了

23 _canSend.Release();

24

25 return value;

26 }

27

28 public void Send(T value)

29 {

30 // 如果是非緩沖的情況,則為阻塞式的,需要等待已有的值被一個(gè) Receiver 接受完,

31 // 才能發(fā)送新值,不能連續(xù) Send

32 _canSend.WaitOne();

33 _value = value;

34

35 // 通知可以接收了

36 _canReceive.Release();

37 }

38 }

接下來粗略的模擬實(shí)現(xiàn) goroutine 的語(yǔ)法:

view sourceprint?01 public static class GoLang

02 {

03 /// <summary>

04 /// 先簡(jiǎn)單的用線程來模擬 goroutine. 因?yàn)槭褂?channel 通信,所以

05 /// 不需考慮線程之間的數(shù)據(jù)共享/同步問題

06 /// </summary>

07 /// <param name="action"></param>

08 public static void go(Action action)

09 {

10 new Thread(new ThreadStart(action)).Start();

11 }

12

13 }

有了這些,我們可以寫一個(gè) test case 來驗(yàn)證了。下面的代碼簡(jiǎn)單的創(chuàng)建一個(gè)并發(fā)的 routine,分別做整數(shù)的 send, receive 操作,以驗(yàn)證是否能正確的發(fā)送和接受值:

view sourceprint?01 /// <summary>

02 /// 測(cè)試多個(gè) Sender 多個(gè) Receiver 同時(shí)在一個(gè) channel 上發(fā)送/接受消息

03 /// </summary>

04 private static void Test1()

05 {

06 var ch = new Channel<int>();

07

08 // 啟動(dòng)多個(gè) Sender

09 GoLang.go(() =>

10 {

11 var id = Thread.CurrentThread.ManagedThreadId;

12 for (var i = 0; i < 7; i++)

13 {

14 Thread.Sleep(new Random((int)DateTime.Now.Ticks).Next(3000));

15 Console.WriteLine("線程{0}發(fā)送值: {1}", id, i);

16 ch.Send(i);

17 }

18 });

19

20 GoLang.go(() =>

21 {

22 var id = Thread.CurrentThread.ManagedThreadId;

23 for (var i = 7; i < 15; i++)

24 {

25 Thread.Sleep(new Random((int)DateTime.Now.Ticks).Next(3000));

26 Console.WriteLine("線程{0}發(fā)送值: {1}", id, i);

27 ch.Send(i);

28 }

29 });

30

31 // 啟動(dòng)多個(gè) Receiver

32 GoLang.go(() =>

33 {

34 var id = Thread.CurrentThread.ManagedThreadId;

35 for (var i = 0; i < 5; i++)

36 {

37 //Console.WriteLine("線程{0}阻塞", id);

38 var value = ch.Receive();

39 Console.WriteLine("線程{0}獲得值: {1}", id, value);

40 }

41 });

42

43 GoLang.go(() =>

44 {

45 var id = Thread.CurrentThread.ManagedThreadId;

46 for (var i = 0; i < 5; i++)

47 {

48 //Console.WriteLine("線程{0}阻塞", id);

49 var value = ch.Receive();

50 Console.WriteLine("線程{0}獲得值: {1}", id, value);

51 }

52 });

53

54 GoLang.go(() =>

55 {

56 var id = Thread.CurrentThread.ManagedThreadId;

57 for (var i = 0; i < 5; i++)

58 {

59 //Console.WriteLine("線程{0}阻塞", id);

60 var value = ch.Receive();

61 Console.WriteLine("線程{0}獲得值: {1}", id, value);

62 }

63 });

64 }

再嘗試實(shí)現(xiàn)一下 Go 語(yǔ)言文檔里舉出的一個(gè)例子 - 篩法求素?cái)?shù):

(見:http://golang.org/doc/go_tutorial.html, Prime numbers)

view sourceprint?01 public class PrimeNumbers

02 {

03 public void Main()

04 {

05 var primes = Sieve();

06

07 // 測(cè)試:打印前100個(gè)素?cái)?shù)

08 for (var i = 0; i < 100; i++)

09 {

10 Console.WriteLine(primes.Receive());

11 }

12 }

13

14 /// <summary>

15 /// 篩法求素?cái)?shù)

16 /// </summary>

17 /// <returns></returns>

18 Channel<int> Sieve()

19 {

20 var @out = new Channel<int>();

21 GoLang.go(() =>

22 {

23 var ch = Generate();

24 for (; ; )

25 {

26 // 當(dāng)前序列中的第一個(gè)值總是素?cái)?shù)

27 var prime = ch.Receive();

28

29 // 將其發(fā)送到輸出序列的尾部

30 @out.Send(prime);

31

32 // 用這個(gè)素?cái)?shù)對(duì)列表進(jìn)行過濾,在進(jìn)入下一次循環(huán),可以保證至少第一個(gè)數(shù)是素?cái)?shù)

33 ch = Filter(ch, prime);

34 }

35 });

36 return @out;

37 }

38

39 /// <summary>

40 /// 產(chǎn)生從2開始的自然數(shù)的無窮序列,這是原始數(shù)列

41 /// 其開始元素 2 是一個(gè)素?cái)?shù)。

42 /// </summary>

43 /// <returns></returns>

44 Channel<int> Generate()

45 {

46 var ch = new Channel<int>();

47 GoLang.go(() =>

48 {

49 for (var i = 2; ; i++)

50 {

51 ch.Send(i);

52 }

53 });

54 return ch;

55 }

56

57 /// <summary>

58 /// 從輸入 channel 里逐個(gè)讀取值,將不能被 prime 整除

59 /// 的那些發(fā)送到輸出 channel (即用 prime 對(duì) @in 序列進(jìn)行一次篩選)

60 /// </summary>

61 Channel<int> Filter(Channel<int> @in, int prime)

62 {

63 var @out = new Channel<int>();

64 GoLang.go(() =>

65 {

66 for (; ; )

67 {

68 var i = @in.Receive();

69 if (i % prime != 0)

70 {

71 @out.Send(i);

72 }

73 }

74 });

75 return @out;

76 }

77 }

下面是整個(gè)測(cè)試工程的 Main 方法:

view sourceprint?01 class Program

02 {

03 static void Main(string[] args)

04 {

05 Test1();

06

07 new PrimeNumbers().Main();

08

09 Console.ReadLine();

10 }

11 }

因?yàn)榇a中已經(jīng)詳細(xì)注釋了,不多做解釋。可以看到,利用 Channel 的概念(好像和 Reactive Programming 有點(diǎn)關(guān)系?),我們可以更清晰的構(gòu)建多線程或者并發(fā)的應(yīng)用程序。

學(xué)習(xí)其他語(yǔ)言,并不是為了學(xué)習(xí)其特定的語(yǔ)法,而是學(xué)習(xí)一種思想。

    3D建模軟件
    (69)3D建模軟件
    建模軟件是專門針對(duì)于三維立體的建模制圖等相關(guān)的一系列的軟件,建模通俗來講就是通過三維制作軟件通過虛擬三維空間構(gòu)建出具有三維數(shù)據(jù)的模型,這里建模軟件是包含有打印建模軟件動(dòng)畫制作軟件建模相關(guān)教程建模視頻教程建模渲染動(dòng)畫制作教程動(dòng)畫三維動(dòng)畫制作視頻動(dòng)畫制作等相關(guān)的一系列的軟件,這里說道制作,動(dòng)畫游戲公司用會(huì)多一點(diǎn),建模操作簡(jiǎn)單,涉及到動(dòng)作和特效的插件很多,自帶的功能也很強(qiáng)大室內(nèi)設(shè)計(jì)一般會(huì)用到多一點(diǎn)。...更多>>

    相關(guān)評(píng)論

    閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

    • 8 喜歡喜歡
    • 3 頂
    • 1 難過難過
    • 5 囧
    • 3 圍觀圍觀
    • 2 無聊無聊

    熱門評(píng)論

    最新評(píng)論

    發(fā)表評(píng)論 查看所有評(píng)論(1)

    昵稱:
    表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
    字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過審核才能顯示)