西西软件园多重安全检测下载网站、值得信赖的软件下载站!
软件
软件
文章
搜索

首页编程开发C#.NET → 用 C# 实现Google Go 语言中的Channel 和 goroutine 机制

用 C# 实现Google Go 语言中的Channel 和 goroutine 机制

相关软件相关文章发表评论 来源:本站整理时间:2010/8/25 19:35:49字体大小:A-A+

作者:佚名点击:110次评论:1次标签: Google Channel

3D建模(Google SketchUp)8.0.11752 官方免费版
  • 类型:3D 制作大小:35.7M语言:英文 评分:4.1
  • 标签:
立即下载

前段时间尝试了一点 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 /// <summary>

02 /// 先实现简单的没有缓冲的 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 // 通知可以发送新的值了

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 /// <summary>

04 /// 先简单的用线程来模拟 goroutine. 因为使用 channel 通信,所以

05 /// 不需考虑线程之间的数据共享/同步问题

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 }

有了这些,我们可以写一个 test case 来验证了。下面的代码简单的创建一个并发的 routine,分别做整数的 send, receive 操作,以验证是否能正确的发送和接受值:

view sourceprint?01 /// <summary>

02 /// 测试多个 Sender 多个 Receiver 同时在一个 channel 上发送/接受消息

03 /// </summary>

04 private static void Test1()

05 {

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

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

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

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 }

再尝试实现一下 Go 语言文档里举出的一个例子 - 筛法求素数:

(见: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 // 测试:打印前100个素数

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

09 {

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

11 }

12 }

13

14 /// <summary>

15 /// 筛法求素数

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 // 当前序列中的第一个值总是素数

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 /// <summary>

40 /// 产生从2开始的自然数的无穷序列,这是原始数列

41 /// 其开始元素 2 是一个素数。

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 里逐个读取值,将不能被 prime 整除

59 /// 的那些发送到输出 channel (即用 prime 对 @in 序列进行一次筛选)

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 }

下面是整个测试工程的 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 有点关系?),我们可以更清晰的构建多线程或者并发的应用程序。

学习其他语言,并不是为了学习其特定的语法,而是学习一种思想。

    3D建模软件
    (69)3D建模软件
    建模软件是专门针对于三维立体的建模制图等相关的一系列的软件,建模通俗来讲就是通过三维制作软件通过虚拟三维空间构建出具有三维数据的模型,这里建模软件是包含有打印建模软件动画制作软件建模相关教程建模视频教程建模渲染动画制作教程动画三维动画制作视频动画制作等相关的一系列的软件,这里说道制作,动画游戏公司用会多一点,建模操作简单,涉及到动作和特效的插件很多,自带的功能也很强大室内设计一般会用到多一点。...更多>>

    相关评论

    阅读本文后您有什么感想? 已有人给出评价!

    • 8 喜欢喜欢
    • 3 顶
    • 1 难过难过
    • 5 囧
    • 3 围观围观
    • 2 无聊无聊

    热门评论

    最新评论

    发表评论 查看所有评论(1)

    昵称:
    表情: 高兴 可 汗 我不要 害羞 好 下下下 送花 屎 亲亲
    字数: 0/500 (您的评论需要经过审核才能显示)