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中提供的函数接口,能够作到及时释放资源。