十分鍾理解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的語法難懂,而且沒有變量,幾乎所有需要使用循環的地方都得用遞歸。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM