本文和大家重点讨论一下Perl线程的生命周期,主要包括创建Perl线程,join方法和detach方法两大部分内容,希望通过本文的介绍你对Perl线程的生命周期有一定的认识。
Perl线程的生命周期
创建Perl线程
Perl线程作为Perl中的一种实体,其一生可以粗略的分为创建,运行与退出这三个阶段。创建使得Perl线程从无到有,运行则是Perl线程完成其主要工作的阶段,退出自然就是指Perl线程的消亡。Perl线程的运行和普通函数的执行非常类似,有其入口参数,一段特定的代码流程以及执行完毕后返回的一个或一组结果,唯一与普通函数调用的不同之处就在于新建Perl线程的执行与当前Perl线程的执行是并行的。
Perl里创建一个新的Perl线程非常简单,主要有两种方法,他们分别是:
使用threads包的create()方法,例如
清单3.通过create()方法创建Perl线程
- usethreads;
- subsay_hello
- {
- printf("Hellothread!@_.\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("Hellothread!@_\n");
- return(rand(10));
- },
- "param5",
- "param6");
使用async{}块创建Perl线程,例如
清单4.通过async{}块创建Perl线程
- #!/usr/bin/perl
- #
- usethreads;
- my$t4=async{
- printf("Hellothread!\n");
- };
#p#
join方法和detach方法
Perl线程一旦被成功创建,它就立刻开始运行了,这个时候你面临两种选择,分别是join或者detach这个新建Perl线程。当然你也可以什么都不做,不过这可不是一个好习惯,后面我们会解释这是为什么。
我们先来看看join方法,这也许是大多数情况下你想要的。从字面上来理解,join就是把新创建的Perl线程结合到当前的主Perl线程中来,把它当成是主Perl线程的一部分,使他们合二为一。join会触发两个动作,首先,主Perl线程会索取新建Perl线程执行结束以后的返回值;其次,新建Perl线程在执行完毕并返回结果以后会自动释放它自己所占用的系统资源。例如
清单5.使用join()方法收割新建Perl线程
- #!/usr/bin/perl
- #
- usethreads;
- subfunc{
- sleep(1);
- return(rand(10));
- }
- my$t1=threads->create(\&func);
- my$t2=threads->create(\&func);
- printf("dosomethinginthemainthread\n");
- my$t1_res=$t1->join();
- my$t2_res=$t2->join();
- printf("t1_res=$t1_res\nt2_res=$t2_res\n");
由此我们不难发现,调用join的时机是一个十分有趣的问题。如果调用join方法太早,新建Perl线程尚未执行完毕,自然就无法返回任何结果,那么这个时候,主Perl线程就不得不被阻塞,直到新建Perl线程执行完毕之后,才能获得返回值,然后资源会被释放,join才能结束,这在很大程度上破话了Perl线程之间的并行性。相反,如果调用join方法太晚,新建Perl线程早已执行完毕,由于一直没有机会返回结果,它所占用的资源就一直无法得到释放,直到被join为止,这在很大程度上浪费了宝贵的系统资源。因此,join新建Perl线程的最好时机应该是在它刚刚执行完毕的时候,这样既不会阻塞当前Perl线程的执行,又可以及时释放新建Perl线程所占用的系统资源。
我们再来看看detach方法,这也许是最省心省力的处理方法了。从字面上来理解,detach就是把新创建的Perl线程与当前的主Perl线程剥离开来,让它从此和主Perl线程无关。当你使用detach方法的时候,表明主Perl线程并不关心新建Perl线程执行以后返回的结果,新建Perl线程执行完毕后Perl会自动释放它所占用的资源。
一个新建Perl线程一旦被detach以后,就无法再join了。当你使用detach方法剥离Perl线程的时候,有一点需要特别注意,那就是你需要保证被创建的Perl线程先于主Perl线程结束,否则你创建的Perl线程会被迫结束,除非这种结果正是你想要的,否则这也许会造成异常情况的出现,并增加程序调试的难度。
本节的开始我们提到,新Perl线程被创建以后,如果既不join,也不detach不是一个好习惯,这是因为除非明确地调用detach方法剥离Perl线程,Perl会认为你也许要在将来的某一个时间点调用join,所以新建Perl线程的返回值会一直被保存在内存中以备不时之需,它所占用的系统资源也一直不会得到释放。然而实际上,你打算什么也不做,因此宝贵的系统资源直到整个Perl应用结束时才被释放。同时,由于你即没有调用join有没有调用detach,应用结束时Perl还会返回给你一个Perl线程非正常结束的警告。
Perl线程的消亡
大多数情况下,你希望你创建的Perl线程正常退出,这就意味着Perl线程所对应的函数体在执行完毕后返回并释放资源。例如在清单5的示例中,新建Perl线程被join以后的退出过程。可是,如果由于detach不当或者由于主线因某些意外的异常提前结束了,尽管它所创建的Perl线程可能尚未执行完毕,但是他们还是会被强制中止,正所谓皮之不存,毛将焉附。这时你也许会得到一个类似于“Perlexitedwithactivethreads”的警告。
当然,你也可以显示地调用exit()方法来结束一个Perl线程,不过值得注意的是,默认情况下,如果你在一个Perl线程中调用了exit()方法,其他Perl线程都会随之一起结束,在很多情况下,这也许不是你想要的,如果你希望exit()方法只在调用它的Perl线程内生效,那么你在创建该Perl线程的时候就需要设置’exit’=>’thread_only’。例如
清单7.为某个Perl线程设置’exit’=>’thread_only’属性
- #!/usr/bin/perl
- #
- usethreads;
- subsay_hello{
- printf("Hellothread!@_.\n");
- sleep(10);
- printf("Bye\n");
- }
- subquick_exit{
- printf("Iwillbeexitinnotime\n");
- exit(1);
- }
- my$t1=threads->create(\&say_hello,"param1","param2");
- my$t2=threads->create({'exit'=>'thread_only'},\&quick_exit);
- $t1->join();
- $t2->join();
如果你希望每个Perl线程的exit方法都只对自己有效,那么在每次创建一个新Perl线程的时候都去要显式设置’exit’=>’thread_only’属性显然有些麻烦,你也可以在引入threads包的时候设置这个属性在全局范围内有效。
【编辑推荐】