三分钟掌握Actor和CSP模型

开发 前端
本文现在要记录的是另一种基于消息传递的并发模型:CSP(communicating sequential process顺序通信过程)。

go的CSP模型

  • 传统多线程的的共享内存(ShareMemory)模型使用lock,condition等同步原语来强行规定进程的执行顺序。
  • Actor模型,是基于消息传递的并发模型, 强调的是Actor这个工作实体,每个Actor自行决定消息传递的方向(要传递的ActorB),通过消息传递形成流水线。

本文现在要记录的是另一种基于消息传递的并发模型:CSP(communicating sequential process顺序通信过程)。

在CSP模型,worker之间不直接彼此联系,强调信道在消息传递中的作用,不谋求形成流水线。

消息的发送者和接受者通过该信道松耦合,发送者不知道自己消息被哪个接受者消费了,接受者也不知道是从哪个发送者发送的消息。

go的信道

go的信道[1]是golang协程同步和通信的原生方式。

同map,slice一样,channel通过make内置函数初始化并返回引用,引用可认为是常量指针[2]。

两种信道:

1. 无缓冲区信道:读写两端就绪后,才能通信(一方没就绪就阻塞)。

这种方式可以用来在goroutine中进行同步,而不必显式锁或者条件变量。

2. 有缓冲区信道:就有可能不阻塞, 只有buffer满了,写入才会阻塞;只有buffer空了,读才会阻塞。

go的信道暂时先聊到这里。

我们来用以上背景做一道 有意思的面试题吧 。

两个线程轮流打印0到100?

我不会啥算法,思路比较弱智:#两线程#, #打印奇/偶数#, 我先复刻这两个标签。

通过go的无缓冲信道的同步阻塞的能力对齐每一次循环。

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#要完成本题要怎么做?

依旧是#两线程#、#打印奇偶数#, 我没找到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();
}

注意:

  • volatile:提醒编译器或运行时系统不对字段做优化(处于性能性能,编译器/runtime会对同时执行的线程访问的同一字段进行优化,加volatile忽略这种优化 )。
  • Object-->MarshalByRefObject-->WaitHandle-->EventWaitHandle--->AutoResetEvent[3] 本次使用了2个自动重置事件来切换通知,由一个线程通知另外一个线程执行。

引用链接

[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

责任编辑:武晓燕 来源: 精益码农
相关推荐

2021-12-17 07:47:37

IT风险框架

2009-11-09 12:55:43

WCF事务

2023-12-04 18:13:03

GPU编程

2021-10-20 17:44:57

分析模型回归

2020-06-30 10:45:28

Web开发工具

2013-06-28 14:30:26

2017-11-20 10:35:36

2017-11-20 10:37:03

2024-01-16 07:46:14

FutureTask接口用法

2021-04-20 13:59:37

云计算

2023-12-27 08:15:47

Java虚拟线程

2022-02-17 09:24:11

TypeScript编程语言javaScrip

2020-08-17 17:20:36

pythonJAVA代码

2017-01-18 15:38:20

语言

2020-03-08 16:45:58

数据挖掘学习数据量

2024-04-01 09:59:08

消息队列通信微服务

2023-07-25 09:00:27

RocketMQ开源

2021-10-27 05:47:53

通信协议协议网络技术

2009-10-29 16:22:10

VB.NET操作MyS

2023-11-23 19:26:43

点赞
收藏

51CTO技术栈公众号