前文传送门:《三分钟掌握共享内存模型和 Actor模型》, 一直想比较Actor模型与golang的CSP模型,经过一段时间的实战记录了本文。
本文现在要记录的是另一种基于消息传递的并发模型:CSP(communicating sequential process顺序通信过程)。
在CSP模型,worker之间不直接彼此联系,强调信道在消息传递中的作用,不谋求形成流水线。
消息的发送者和接受者通过该信道松耦合,发送者不知道自己消息被哪个接受者消费了,接受者也不知道是从哪个发送者发送的消息。
go的信道[1]是golang协程同步和通信的原生方式。
同map,slice一样,channel通过make内置函数初始化并返回引用,引用可认为是常量指针[2]。
两种信道:
go的信道暂时先聊到这里。
我们来用以上背景做一道 有意思的面试题吧 。
调试多线程的都懂.gif
我不会啥算法,思路比较弱智:#两线程#, #打印奇/偶数#, 我先复刻这两个标签。
package main
import (
"fmt"
"strconv"
"sync"
)
var wg sync.WaitGroup
var ch1 = make(chan struct{})
func main() {
wg.Add(2)
go func() {
defer wg.Done()
for i := 0; i <= 100; i++ {
ch1 <- struct{}{}
if i%2 == 0 { // 偶数
fmt.Println("g0 " + strconv.Itoa(i))
}
}
}()
go func() {
defer wg.Done()
for i := 0; i <= 100; i++ {
<-ch1
if i%2 == 1 { // 奇数
fmt.Println("g1 " + strconv.Itoa(i))
}
}
}()
wg.Wait()
}
题解:两个协程都执行0到100次循环,但是不管哪个线程跑的快,在每次循环输出时均会同步对齐, 每次循环时只输出一个奇/偶值, 这样也不用考虑两个协程的启动顺序。
依旧是#两线程#、#打印奇偶数#, 我没找到C#中能多次对齐线程的能力, 于是使用两线程相互通知的方式。
volatile static int i = 0;
static AutoResetEvent are = new AutoResetEvent(true);
static AutoResetEvent are2 = new AutoResetEvent(false);
public static void Main(String[] args)
{
Thread thread1 = new Thread(() =>
{
for (var i=0;i<=100;i++)
{
are.WaitOne();
if (i % 2 == 0)
{
Console.WriteLine(i + "== 偶数");
}
are2.Set();
}
});
Thread thread2 = new Thread(() =>
{
for (var i = 0; i <= 100; i++)
{
are2.WaitOne();
if (i % 2 == 1)
{
Console.WriteLine(i + "== 奇数");
}
are.Set();
}
});
thread1.Start();
thread2.Start();
Console.ReadKey();
}
注意:
[1]
go的信道: https://www.runoob.com/w3cnote/go-channel-intro.html
[2]
常量指针: https://zhuanlan.zhihu.com/p/133225100
[3]
AutoResetEvent: https://docs.microsoft.com/en-us/dotnet/api/system.threading.autoresetevent?view=net-6.0