十分钟理解Actor模式

简介:

Actor模式是一种并发模型,与另一种模型共享内存完全相反,Actor模型share nothing。所有的线程(或进程)通过消息传递的方式进行合作,这些线程(或进程)称为Actor。共享内存更适合单机多核的并发编程,而且共享带来的问题很多,编程也困难。随着多核时代和分布式系统的到来,共享模型已经不太适合并发编程,因此几十年前就已经出现的Actor模型又重新受到了人们的重视。MapReduce就是一种典型的Actor模式,而在语言级对Actor支持的编程语言Erlang又重新火了起来,Scala也提供了Actor,但是并不是在语言层面支持,Java也有第三方的Actor包,Go语言channel机制也是一种类Actor模型。


单线程编程


单核单机时代一般都是单线程编程,如果把程序比作一个工厂,那么只有一个工人,这个工人负责所有的事情,所有的原料,工具产品等都放到一个地方,因为只有一个人,因此使用一套工具就行,取原料也不用排队等。wKioL1WOK1TARuclAADkQl8bN5U550.jpg


多线程编程-共享内存


到了多核时代,有多个工人,这些工人共同使用一个仓库和车间,干什么都要排队。比如我要从一块钢料切出一块来用,我得等别人先用完。有个扳手,另一个人在用,我得等他用完。两个人都要用一个切割机从一块钢材切一块钢铁下来用,但是一个人拿到了钢材,一个人拿到了切割机,他们互相都不退让,结果谁都干不了活。

wKiom1WOKpHgGDoXAAFY8MuvOgA842.jpg

假如现在有一个任务,找100000以内的素数的个数,最多使用是个线程,如果用共享内存的方法,可以用下面的代码实现。可以看到,这些线程共享了currentNum和totalPrimeCount,对它们做操作时必须上锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public  class  PrimeCount  implements  Runnable {
    
     private  int  currentNum =  2 ;   //从2开始找
     private  int  totalPrimeCount =  0 //当前已经找到的
     
     //取一个数,不能重复,最大到100000
     private  int  incrCurrentNum() { 
         synchronized  ( this ) {      //如果不用锁,必然会出错。
             if (currentNum >  100000 ) {
                 return  - 1 ;
             else  {
                 int  result = currentNum;
                 currentNum++;
                 return  result;
             }  
         }
     }
     
    //把某个线程找到的素数个数加上
     private  void  accPrimeCount( int  count) { 
         synchronized  ( this ) {
             totalPrimeCount += count;
         }
     }
     
     @Override
      //一直取数并判断是否为素数,取不到了就把找到的个数累加
     public  void  run() { 
         int  primeCount =  0 ;
         int  num;
         while ((num=incrCurrentNum()) != - 1 ) {
             if (isPrime(num)) {
                 primeCount++;
             }
         }
         accPrimeCount(primeCount);
     }
     private  boolean  isPrime( int  num) {
         for ( int  i =  2 ; i < num; i++) {
             if (num % i ==  0 ) {
                 return  false ;
             }
         }
         return  true ;
    
     
     @SuppressWarnings ( "static-access" )
     public  static  void  main(String[] args){
         PrimeCount pc =  new  PrimeCount();
         for ( int  i =  0 ; i <  10 ; i++) {
             new  Thread(pc).start();
         }
         try  {
             Thread.currentThread().sleep( 5000 );
         catch  (InterruptedException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
         System.out.println(pc.getTotalPrimeCount());
     }
     
     public  int  getTotalPrimeCount() {
         return  totalPrimeCount;
     }
  
}


多线程/分布式编程-Actor模型


到了分布式系统时代,工厂已经用流水线了,每个人都有明确分工,这就是Actor模式。每个线程都是一个Actor,这些Actor不共享任何内存,所有的数据都是通过消息传递的方式进行的。

wKiom1WOKyPBDRjpAAGSeStUphQ651.jpg


如果用Actor模型实现统计素数个数,那么我们需要1个actor做原料的分发,就是提供要处理的整数,然后10个actor加工,每次从分发actor那里拿一个整数进行加工,最终把加工出来的半成品发给组装actor,组装actor把10个加工actor的结果汇总输出。

用scala实现,下面是工程的结构:

wKioL1WdJcqjpAFzAAD5VFWMxWE737.jpg

这是它们传递的消息,有一些指令,剩下的都是Int数据:

wKiom1WdJGejogeEAADFcGfvlHM213.jpg

一个Actor的代码结构一般是下面这种结构,不停的接受消息并处理,没有消息就等待:

wKioL1WdJniAb7CiAACCrB88e3w417.jpg

组装者代码:

wKiom1WdJQuC2KISAAD_TzvsHmU128.jpg

分发者代码:

wKiom1WdJT6ToafUAAHzO5b-wbg835.jpg

加工者代码:

wKioL1WdJ8aQb3cnAAH3zqiR_OY263.jpg

主线程代码:

wKiom1WdJkDCESVPAAHn1a3-nqU963.jpg

工程代码可以在附件中下载。这个代码实现的效果与前面用Java实现的是一样的,但是各个线程没有共享内存,也没有锁,这样开发起来容易,而且更适合分布式编程,因为分布式编程本身就不适合共享内存。Scala的Actor不能原生的支持分布式,但是Erlang可以,使用Erlang的Actor,分布式编程就和本地编程基本一样。但是Erlang的语法难懂,而且没有变量,几乎所有需要使用循环的地方都得用递归。


本文转自nxlhero 51CTO博客,原文链接:http://blog.51cto.com/nxlhero/1666250,如需转载请自行联系原作者

相关文章
|
8天前
|
Kubernetes 调度 容器
【赵渝强老师】K8s的Job控制器多工作队列的并行方式
Kubernetes Job 是一次性任务控制器,用于控制 Pod 中的容器执行特定任务。本文介绍了 Job 控制器的工作原理、运行方式及多工作队列并行执行的示例。示例中创建了 5 个作业,以 3 个队列并行执行,整个过程需 2 分钟。文中还提供了详细的 YAML 文件配置和执行命令。
|
3月前
|
消息中间件 负载均衡 Kafka
【Kafka消费秘籍】深入了解消费者组与独立模式,掌握消息消费的两种超能力!
【8月更文挑战第24天】Apache Kafka是一款高性能的分布式消息系统,支持灵活多样的消费模型以适应不同的应用场景。消息按主题组织,每个主题可划分为多个分区,确保消息顺序性。本文深入探讨了Kafka中的两大核心消费模式:消费者组(Consumer Group)和独立消费者(Standalone Consumer)。消费者组允许多个消费者协同工作,实现负载均衡及故障恢复,是最常用的消费模式。独立消费者模式则适用于需要高度定制化处理逻辑的场景,如消息重放等。通过对比这两种模式的特点和提供的示例代码,开发者可以根据具体需求选择最合适的消费策略,从而更好地利用Kafka构建高效的数据流应用程序。
87 3
|
6月前
|
消息中间件 网络协议 Kafka
Kafka【付诸实践 02】消费者和消费者群组+创建消费者实例+提交偏移量(自动、手动)+监听分区再平衡+独立的消费者+消费者其他属性说明(实例源码粘贴可用)【一篇学会使用Kafka消费者】
【2月更文挑战第21天】Kafka【付诸实践 02】消费者和消费者群组+创建消费者实例+提交偏移量(自动、手动)+监听分区再平衡+独立的消费者+消费者其他属性说明(实例源码粘贴可用)【一篇学会使用Kafka消费者】
204 3
|
6月前
|
消息中间件 监控 中间件
【工作中问题解决实践 十一】Kafka消费者消费堆积且频繁rebalance
【工作中问题解决实践 十一】Kafka消费者消费堆积且频繁rebalance
432 0
|
程序员
同步模式之犹豫模式Balking
同步模式之犹豫模式Balking是一种多线程编程中的同步模式。在该模式中,线程在执行操作之前会先检查某些条件,如果发现在执行操作之前会导致某些不良后果,则该线程会放弃执行该操作,避免出现问题。
109 0
同步模式之犹豫模式Balking
|
消息中间件 前端开发 Java
Actor 模型工作机制和消息机制 | 学习笔记
快速学习 Actor 模型工作机制和消息机制
Actor 模型工作机制和消息机制 | 学习笔记
|
消息中间件 关系型数据库 MySQL
|
消息中间件 存储 安全
Rebalance&多线程实例消费(十二)
Rebalance&多线程实例消费(十二)
|
消息中间件 安全 Java
答读者问:Kafka顺序消费吞吐量下降该如何优化?
答读者问:Kafka顺序消费吞吐量下降该如何优化?
答读者问:Kafka顺序消费吞吐量下降该如何优化?
|
算法 编译器 Go
三分钟掌握Actor和CSP模型
一直想比较Actor模型与golang的CSP模型,经过一段时间的实战记录了本文。
三分钟掌握Actor和CSP模型