新物网

当前位置:首页 > 百科

百科

Chanel和goroutine机制用C#完成GoogleGo语言表达中的Chanel和goroutine

时间:2023-10-22 11:40:04 雅雅
前段时间试了一点 Google 的 Go 语言表达,感觉很多其他特点都很好。Go 语言表达致力于结合传统编译程序静态语言和解释动态语言的优势,找到平衡。从而创造出快速(编译执行)、省力的语言表达(动态语言通常简单方便)。与此同时,Go 语言表达还具有促进并发编程的丰富特点,这在当今多核非常流行

前段时间试了一点 Google 的 Go 语言表达,感觉很多其他特点都很好。Go 语言表达致力于结合传统编译程序静态语言和解释动态语言的优势,找到平衡。从而创造出快速(编译执行)、省力的语言表达(动态语言通常简单方便)。与此同时,Go 语言表达还具有促进并发编程的丰富特点,这在当今多核非常流行的情况下是一个非常重要和强大的功能。

Go 语言表达的高并发特征主要包括 goroutine, channel 等。goroutine - 它可以大致解释为一个轻量级的过程(或微过程),它是一个“可并行执行的函数在同一详细地址空间内分配”。同时,它是轻量级的,不需要像分配过程一样分配单独的堆栈空间。因此,理论上,我们可以很容易地分配多个 goroutine, 并发执行,成本远小于线程同步程序流程,从而使程序流程适用于更大的并发性。

channel - 说白了,就是安全通道。安全通道的目的是传输数据。每个人都可以在一个通道上发送数据信息(Send)和理解(Receive)实际操作。对非缓存的 channel 来讲,Receive 在执行该方法时,将判断安全通道上是否有值,如果没有,将等待(堵塞),直到有值。一样,在 channel 上面有值,而被一个 Receiver 接纳时,Send 方法也会被堵塞,直到 Channel 变空。这样,就可以根据一个简单的系统来保证 Send 和 Receive 它总是在不同的时间实施,只有 Send 后才能 Receive. 这避免了常规多线程编程中的信息共享问题。如同 Go 语言表达文本文档一句话常说:

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

不需要根据共享内存进行沟通;而是通过沟通共享内存。

在常规的多线程编程中,人们总是定义一些类变量。如果多个线程可以同时浏览自变量,则需要锁定。这样就带来了一定程序编写的多样性,如果编码中写的稍有bug,就会导致读/提到不正确的数值。

并且通过 channel 为了沟通,我们得到了一种更清晰的沟通方式。两个过程(或 goroutine)如果你想读写能力相同的数据信息,你应该创建一个安全通道。双方通过这个安全通道实施 Send / Receive 操作可以设置值或选择值,相对来说,不容易出错。

为了更深入地理解这一原则,我试过了 C# 获得类似的效果。

相较于 goroutine, 我没有完成微过程,因为这必须是一个更复杂的调度机制(准备进一步研究这些方面)。我们可以临时使用 Thread 简单模拟模拟。

而 Channel, 则以 Semaphone 操纵同步 Send / Receive 就行了。

首先,让我们完成一个简单的工作 Channel,上面已经说过概念:

view sourceprint?01 ///

02 /// 先完成简单,没有缓存。 Channel.

03 ///

04 ///

05 public class Channel

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 // 一个新的通知值得推送

23 _canSend.Release();

24

25 return value;

26 }

27

28 public void Send(T value)

29 {

30 // 若为非缓存现象,它是堵塞式的,需要等待已经有数值的一个 Receiver 接纳完,

31 // 只有这样,才能推送新值,不可以持续 Send

32 _canSend.WaitOne();

33 _value = value;

34

35 // 可以接收通知

36 _canReceive.Release();

37 }

38 }

下面粗略的模拟模拟完成 goroutine 的词法:

view sourceprint?01 public static class GoLang

02 {

03 ///

04 /// 首先,简单地使用过程模拟 goroutine. 由于使用 channel 通讯,因此

05 /// 过程之间的信息共享/同步问题不应考虑

06 ///

07 ///

08 public static void go(Action action)

09 {

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

11 }

12

13 }

有这些,我们可以写一个 test case 来检验了。下面的代码可以简单地创建并发代码 routine,各自做整数金额 send, receive 实际操作,以验证正确的发送和理解值:

view sourceprint?01 ///

02 /// 检测好几个 Sender 好几个 Receiver 同时在一个 channel 推送/接收信息

03 ///

04 private static void Test1()

05 {

06 var ch = new Channel();

07

08 // 运行好几个 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}推送值: {1}", id, i);

16 ch.Send(i);

17 }

18 });

19

< 7; i )

13 {

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

15 Console.WriteLine('线程{0}推送值: {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}推送值: {1}", id, i);

27 ch.Send(i);

28 }

29 });

30

31 // 运行好几个 Receiver

< 15; i )

24 {

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

26 Console.WriteLine('线程{0}推送值: {1}', id, i);

27 ch.Send(i);

28 }

29 });

30

31 // 运行好几个 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

< 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

< 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 }

再试一次 Go 语言表达文档中列出的一个例子 - 筛法要素数:

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

view sourceprint?01 public class PrimeNumbers

02 {

03 public voidMain()

04 {

05 var primes = Sieve();

06

07 // 检测:打印前100个素数

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

09 {

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

11 }

12 }

13

14 ///

15 /// 筛法求素数 16 ///

17 ///

18 Channel Sieve()

19 {

20 var @out = new Channel

();

21 GoLang.go(() =>

22 {

23 var ch = Generate();

24 for (; ; )

25 {

26 // 现阶段编码序列中的第一个值一直是素数

27 var prime = ch.Receive();

28

29 // 将其发送到导出序列的尾端

30 @out.Send(prime);

31

32 // 用这个素数处理目录,进入下一个循环系统可以确保至少第一个数是素数

33 ch = Filter(ch, prime);

34 }

35 });

36 return @out;

37 }

38

39 ///

40 /// 自然数的无限编码序列从2开始,这也是初始等差数列

41 /// 其逐渐原素 2 是素数。

42 ///

43 ///

44 Channel

Generate()

45 {

46 var ch = new Channel

();

47 GoLang.go(() =>

48 {

49 for (var i = 2; ; i )

50 {

51 ch.Send(i);

52 }

53 });

54 return ch;

55 }

56

57 ///

58 /// 从键入 channel 逐一阅读选值,将无法被 prime 能整除

59 /// 这些发送到导出 channel (既用 prime 对 @in 一次选择序列)

60 ///

61 Channel Filter(Channel @in, int prime) 62 { 63 var @out = new Channel();

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 } 以下是所有检测项目 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 } 因为代码中已经详细注解了,不要多做表达。可见,可用 Channel 这个概念(仿佛和 Reactive Programming 有点关系?可见,可用 Channel 这个概念(仿佛和 Reactive Programming 有点关系吗?),我们可以更清楚地构建线程同步或高并发应用程序。