• 三分钟掌控Actor模型和CSP模型


    回顾一下前文《三分钟掌握共享内存模型和 Actor模型》

    Actor vs CSP模型

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

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

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

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

    go的信道

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

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

    两种信道:

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

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

    1. 有缓冲区信道:就有可能不阻塞, 只有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#要完成本题要怎么做?

    依旧是#两线程#、#打印奇偶数#。

    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
      本次使用了2个自动重置事件来切换通知,由一个线程通知另外一个线程执行。
  • 相关阅读:
    SpringCloud初体验:四、API GateWay 服务网关
    SpringCloud初体验:三、Feign 服务间调用(FeignClient)、负载均衡(Ribbon)、容错/降级处理(Hystrix)
    SpringCloud初体验:二、Config 统一配置管理中心
    SpringCloud初体验:一、Eureka 服务的注册与发现
    PHP 通过实现 Iterator(迭代器)接口来读取大文件文本
    SpringCloud初体验:前言
    springboot利用MockMvc测试controller控制器
    vue通过(NGINX)部署在子目录或者二级目录实践
    PHP 设计模式 原型模式(Prototype)之深/浅拷贝
    PHPstorm配置PHPunit对composer引入的php代码进行单元测试
  • 原文地址:https://www.cnblogs.com/JulianHuang/p/16056120.html
Copyright © 2020-2023  润新知