2000 年 5 月發佈的 Perl v5.6.0 中開始引入了一個全新的線程模型,即 interpreter threads, 或稱爲 ithreads,也正是在這個版本的發佈申明中第一次提出了 5005threads 線程模型未來可能會被禁用的問題。 數據結構
use threads;#導入threads包 多線程
$thread = threads->create(\fun,para1,para2); async
$result=$thread->join();#召回線程的返回,無參數,$result變量爲返回值,
$thread->detach();#使分線程和主線程分離,無返回值,無參數。調用此方法後就不能再其餘線程調用關於這個線程的join()方法了。 函數
threads->list
threads->list(threads::all) ui
With no arguments (or using threads::all) and in a list context, returns a list of all non-joined, non-detached threads objects. In a scalar context, returns a count of the same. spa
threads->list(threads::running)
threads->list(threads:joinable) 線程
With a true argument (using threads::running), returns a list of all non-joined, non-detached threads objects that are still running.
With a false argument (using threads::joinable), returns a list of all non-joined, non-detached threads objects that have finished running (i.e., for which ->join() will not block).
scala
use warnings; use threads; use threads::shared; use Data::Dumper; $|=1;#Build-in variables, if NZ, the output will be printed out without caching my @array:shared = (); sub test{ my $i = shift; my @x=(1..99999); sleep 2*$i; print "$i:start:"; for(my $j =$i;$j<10;$j++){ print $j." :"; #push $j,@array; } printf "%s run to the end.\n",threads->tid(); } for(reverse(0..10)){ threads->create(\&test,$_); } my $monitor = async{#to supervise the state of the child threads sleep(1); while(1){ for(threads->list()){ #printf "%s join ? %s \n",$_->tid(),$_->is_joinable()?'true':'false'; } sleep 2; } }; $monitor->detach(); #for(threads->list()){ #$_->join(); #} while(threads->list()){ $_->join() for threads->list(threads::joinable); }
0:start:0:1:2:3:4:5:6:7:8:9:11 run to the end. code
1:start:1:2:3:4:5:6:7:8:9:10 run to the end. 接口
2:start:2:3:4:5:6:7:8:9:9 run to the end.
3:start:3:4:5:6:7:8:9:8 run to the end.
4:start:4:5:6:7:8:9:7 run to the end.
5:start:5:6:7:8:9:6 run to the end.
6:start:6:7:8:9:5 run to the end.
7:start:7:8:9:4 run to the end.
8:start:8:9:3 run to the end.
9:start:9:2 run to the end.
10:start:1 run to the end.
perl線程的生命週期
建立線程
線程做爲Perl中的一種實體,其週期包括建立,運行和退出。
Perl裏建立一個新的線程很是簡單,主要有兩種方法,他們分別是:
一、使用threads包的create方法
use threads; sub say_hello { printf("Hello thread! @_.\n"); return( rand(10) ); } my $t1 = threads->create( \&say_hello, "param1", "param2" ); my $t2 = threads->create( "say_hello", "param3", "param4" ); my $t3 = threads->create( sub { printf("Hello thread! @_\n"); return( rand(10) ); }, "param5", "param6" );二、經過async{}塊建立匿名線程
#!/usr/bin/perl # use threads; my $t4 = async{ printf("Hello thread!\n"); };join方法和detach方法
線程一旦被成功建立,它就馬上開始運行,面臨兩種選擇join或者detach方法。
從字面上,join就是把新建立的線程結合到當前的主線程中來,把它當成是主線程的一部分,使他們合二爲一。join會出發兩個動做,首先,主線程會索取新建線程執行結束之後的返回值,其次,新建線程在執行完畢後並返回結果之後會自動釋放它本身所佔用的系統資源。
#!/usr/bin/perl # use threads; sub func { sleep(1); return(rand(10)); } my $t1 = threads->create( \&func ); my $t2 = threads->create( \&func ); printf("do something in the main thread\n"); my $t1_res = $t1->join(); my $t2_res = $t2->join(); printf("t1_res = $t1_res\nt2_res = $t2_res\n");若是調用 join 方法太早,新建線程還沒有執行完畢,天然就沒法返回任何結果,那麼這個時候,主線程就不得不被阻塞,直到新建線程執行完畢以後,才能得到返回值,而後資源會被釋放。 join 才能結束,這在很大程度上破話了線程之間的並行性。相反,若是調用 join 方法太晚,新建線程早已執行完畢,因爲一直沒有機會返回結果,它所佔用的資源就一直沒法獲得釋放,直到被 join 爲止,這在很大程度上浪費了寶貴的系統資源。所以,join 新建線程的最好時機應該是在它剛剛執行完畢的時候,這樣既不會阻塞當前線程的執行,又能夠及時釋放新建線程所佔用的系統資源。??????
咱們再來看看 detach 方法,這也許是最省心省力的處理方法了。從字面上來理解,detach 就是把新建立的線程與當前的主線程剝離開來,讓它今後和主線程無關。當你使用 detach 方法的時候,代表主線程並不關心新建線程執行之後返回的結果,新建線程執行完畢後 Perl 會自動釋放它所佔用的資源。
#!/usr/bin/perl # use threads; use Config; sub say_hello { my ( $name ) = @_; printf("Hello World! I am $name.\n"); } my $t1 = threads->create( \&say_hello, "Alex" ); $t1->detach(); printf("doing something in main thread\n"); sleep(1);本節的開始咱們提到,新線程被建立之後,若是既不 join,也不 detach 不是一個好習慣,這是由於除非明確地調用 detach 方法剝離線程,Perl 會認爲你也許要在未來的某一個時間點調用 join,因此新建線程的返回值會一直被保存在內存中以備不時之需,它所佔用的系統資源也一直不會獲得釋放。然而實際上,你打算什麼也不作,所以寶貴的系統資源直到整個 Perl 應用結束時才被釋放。同時,因爲你即沒有調用 join 有沒有調用 detach,應用結束時 Perl 還會返回給你一個線程非正常結束的警告。
線程的消亡
大多數狀況下,你但願你建立的線程正常退出,這就意味着線程所對應的函數體在執行完畢後返回並釋放資源。例如在清單 5 的示例中,新建線程被 join 之後的退出過程。但是,若是因爲 detach 不當或者因爲主線因某些意外的異常提早結束了,儘管它所建立的線程可能還沒有執行完畢,可是他們仍是會被強制停止,正所謂皮之不存,毛將焉附。這時你也許會獲得一個相似於「Perl exited with active threads」的警告。
固然,你也能夠顯示地調用 exit() 方法來結束一個線程,不過值得注意的是,默認狀況下,若是你在一個線程中調用了 exit() 方法, 其餘線程都會隨之一塊兒結束,在不少狀況下,這也許不是你想要的,若是你但願 exit() 方法只在調用它的線程內生效,那麼你在建立該線程的時候就須要設置’ exit ’ => ’ thread_only ’。例如
#!/usr/bin/perl # use threads; sub say_hello { printf("Hello thread! @_.\n"); sleep(10); printf("Bye\n"); } sub quick_exit { printf("I will be exit in no time\n"); exit(1); } my $t1 = threads->create( \&say_hello, "param1", "param2" ); my $t2 = threads->create( {'exit'=>'thread_only'}, \&quick_exit ); $t1->join(); $t2->join();若是你但願每一個線程的 exit 方法都只對本身有效,那麼在每次建立一個新線程的時候都去要顯式設置’ exit ’ => ’ thread_only ’屬性顯然有些麻煩,你也能夠在引入 threads 包的時候設置這個屬性在全局範圍內有效,例如
use threads ('exit' => 'threads_only'); sub func { ... if( $condition ) { exit(1); } } my $t1 = threads->create( \&func ); my $t2 = threads->create( \&func ); $t1->join(); $t2->join();
threads::shared
和如今大多數線程模型不一樣,在 Perl ithreads 線程模型中,默認狀況下任何數據結構都不是共享的。當一個新線程被建立之後,它就已經包含了當前全部數據結構的一份私有拷貝,新建線程中對這份拷貝的數據結構的任何操做都不會在其餘線程中有效。所以,若是須要使用任何共享的數據,都必須顯式地申明。threads::shared 包能夠用來實現線程間共享數據的目的。
#!/usr/bin/perl # use threads; use threads::shared; use strict; my $var :shared = 0; # use :share tag to define my @array :shared = (); # use :share tag to define my %hash = (); share(%hash); # use share() funtion to define sub start { $var = 100; @array[0] = 200; @array[1] = 201; $hash{'1'} = 301; $hash{'2'} = 302; } sub verify { sleep(1); # make sure thread t1 execute firstly printf("var = $var\n"); # var=100 for(my $i = 0; $i < scalar(@array); $i++) { printf("array[$i] = $array[$i]\n"); # array[0]=200; array[1]=201 } foreach my $key ( sort( keys(%hash) ) ) { printf("hash{$key} = $hash{$key}\n"); # hash{1}=301; hash{2}=302 } } my $t1 = threads->create( \&start ); my $t2 = threads->create( \&verify ); $t1->join(); $t2->join();
多線程既然有了共享數據,那麼就必須對共享數據進行當心地訪問
Perl建立線程有兩種方式,正常經過threads->create建立線程,用async建立一個調用匿名過程的線程;
線程共享變量須要使用threads::shared,共享變量只能存儲scalar,共享變量的引用,若是存儲List Hash引用須要shared_clone([@list])shared_clone({%hash})。
線程建立後最好使用join或者detach,不然在退出時會有warning。
線程的join方式,使用threads中提供的函數接口,能夠作到及時釋放資源。