您好,登錄后才能下訂單哦!
這篇文章主要介紹perl多線程的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
perl 多線程
1、并行系列一
線程是一個單一的執行流程,它是所有程序執行過程中最小的控制單位,即能被 CPU 所調度的最小任務單元。線程與進程之間既有聯系,又完全不同。簡單地說,一個線程必然屬于某一個進程,而一個進程包含至少一個或者多個線程。早期的計算機系統一次只能運行一個程序,因此,當有多個程序需要執行的時候,唯一的辦法就是讓它們排成隊,按順序串行執行。進程的出現打破了這種格局,CPU 資源按時間片被分割開來,分配給不同的進程使用。這樣一來,從微觀上看進程的執行雖然仍是串行的,但是從宏觀上看,不同的程序已經是在并行執行了。如果我們把同樣的思想運用到進程上,很自然地就會把進程再細分成更小的執行單位,即線程。由于一個進程又往往需要同時執行多個類似的任務,因此這些被細分的線程之間可以共享相同的代碼段,數據段和文件句柄等資源。有了進程,我們可以在一臺單 CPU 計算機系統上同時運行 Firefox 和 Microsoft Office Word 等多個程序;有了線程,我們可以使 Firefox 在不同的標簽里同時加載多個不同的頁面,在 Office Word 里編輯文檔的同時進行語法錯誤檢查。因此,線程給我們帶來了更高的 CPU 利用率、更快速的程序響應、更經濟地資源使用方式和對多 CPU 的體系結構更良好的適應性。
關于多線程的詳細講解,可參看:perl 線程模型講解(http://it.chinawin.net/softwaredev/article-124a1.html)
--------------------------------------------------------------
perl中的多線程模塊
5.8以后的版本,的多線程模塊可參看perldoc(http://perldoc.perl.org/threads.html)
---------------------------------------------------------------
perl的多線程實例:
涉及語言:Perl
所用模塊:threads
模塊中的方法: threads->create(),創建一個新線程;
threads->join(),收割已經創建的線程;
threads->list(threads::all),返回所有已經創建的線程;
threads->is_joinable(),返回目標線程是否已經完成,等待join;
其他的在perldoc上了,字母文字好長。就不翻譯了。
--------------------------------------------------------
#begin use threads; #聲明模塊; use warnings;use strict; print localtime(time),"\n"; #輸出系統時間; my $j=0; my $thread; while() { last if($j>=10);這里控制一下任務數量,共10個; #控制創建的線程數,這里是5,scalar函數返回列表threads->list()元素的個數; while(scalar(threads->list())<5) { $j++; #創建一個線程,這個線程其實就是調用(引用)函數“ss”; #函數‘ss’包含兩個參數($j和$j); threads->new(\&ss,$j,$j); } foreach $thread(threads->list(threads::all)) { if($thread->is_joinable()) #判斷線程是否運行完成; { $thread->join(); #輸出中間結果; print scalar(threads->list()),"\t$j\t",localtime(time),"\n"; } } } #join掉剩下的線程(因為在while中當j=10時,還有4個線程正在運行,但是此時程序將退出while循,所以在這里需要額外程序join掉剩下的4個線程) foreach $thread(threads->list(threads::all)) { $thread->join();print scalar(threads->list()),"\t$j\t",localtime(time),"\n"; } #輸出程序結束的時間,和程序開始運行時間比較,看程序運行性能; print localtime(time),"\n"; #下面就是每個線程引用的函數; sub ss() { my ($t,$s)=@_; sleep($t); #sleep函數,睡覺;以秒為單位; print "$s\t"; }
----------------------------------------------------
結果:
第一列表示程序已經完成的任務數,第二列表示正在運行的線程數-1(join掉一個了),第三列表示在收割掉一個線程后新添加的任務,最后一列表示完成一個線程時的系統時間。
------------------------------------------------------------
多線程運行性能
如果單獨運行這10個任務,所需要的時間為:1+2+3+4++10=55s;
采用多線程運行(5個)的話,需要的時間為:54-39=16s;
-------------------------------------------------------------
運行過程
簡要描述一下程序運行過程,以便更深入理解多線程的概念,呵呵
程序共要運行10個任務,第一個任務的作用是暫停程序1s(sleep(1));第二個任務是暫停程序2s(sleep(2));以此類推,第十個任務是暫停程序10s;
時間(s) 任務
0 1,2,3,4,5(程序初始,5個線程同時運行,需要時間最長的是線程5(5s))
1 2,3,4,5,6(經過1s后,第一個任務已經完成,被join掉,同時添加新任務6)
2 3,4,5,6,7(同上)
3 4,5,6,7,8
4 5,6,7,8,9
5 6,7,8,9,10
7-end join所有剩下的線程(所有任務都已經添加,程序中while循環退出)
方法$thread->is_joinable()的作用
前面已經說了,這個方法是用來判斷線程是否已經運行完成,處于等待join的狀態。當需要處理多個任務,但這些任務完成需要的時間又不一樣時,這個方法就顯得特別重要。
還是以上面的程序為例。程序初始運行時創建5個線程。第一個線程所需時間最短,為1s。第五個線程所需時間最長5s。如果不適用$thread->is_joinable()而直接join這五個線程的話,如下:
foreach $thread(threads->list(threads::all)) { $thread->join(); }
結果是:主程序處于等待狀態。在1s后,第一個線程被join,主程序依然處于等待,2s后第二個線程被join,主程序等待。知道5s后第五個線程被join,主程序通暢,重新創建下一組線程(5個)。顯然這個過程不能最大話利用CPU的資源。當第一個線程被join后,雖然程序中只有4個線程在運行,但是由于主程序處于等待狀態,新的線程不會被創建。
最佳的方法就是判斷線程是否可以被join。如上面的程序所寫的。這樣可以保證程序運行過程中始終是5個線程,最大化的利用CPU資源。
-------------------------------------------------------
實例
說了這么多,多線程在生物信息中到底可以怎么來運用,下面給一個簡單的實例。從KEGG數據庫(http://www.genome.jp/kegg/)上搜索同源序列。
所需文件:seqname.txt(用于存放需要搜索的序列KEGG名稱);
源碼:
use strict;use warnings;use threads; use SOAP::Lite; use Cwd; my $path=getcwd; my $wsdl = 'http://soap.genome.jp/KEGG.wsdl'; my $serv = SOAP::Lite->service($wsdl); open(F,"K00006.txt"); my @names=<F>; chomp @names; close(F); my $i=0; my $thread; print localtime(time); while($i<scalar(@names)) { while(scalar(threads->list())<10) { threads->new(\&orgfile,$names[$i]); $i++; } foreach $thread(threads->list(threads::all)) { if($thread->is_joinable()) { $thread->join(); } } } foreach $thread(threads->list(threads::all)) { $thread->join(); } print localtime(time); sub orgfile { my($seq)=@_; my $offset = 1; my $limit = 100; my $top5 = $serv->get_best_neighbors_by_gene($seq, $offset,$limit); $seq=~s/://; open(F,">$seq.txt"); foreach my $hit (@{$top5}) { print F "$hit->{genes_id1}\t$hit->{genes_id2}\t$hit->{sw_score}\n"; } close(F); print "$seq\n"; }
------------------------------------------------
END;
2、并行開發系列二
在當今多核CPU主流的形勢下,多核并行程序提供了最大話利用CPU的可能性。perl自5.6版本后開始提供ithread模塊專門為perl的多線程提供技術支持。在perl的多線程一文中,我們初步探討了perl的多線程技術。里面使用了is_joinable方法,來判斷目標線程是否已經執行完成,并處于等待join的狀態。程序源碼如下(http://blog.sina.com.cn/s/blog_7ea1e7fc0100r61x.html):
use threads;use warnings;use strict; print localtime(time),"\n"; my $j=0; my $thread; while() { last if($j>=10); while(scalar(threads->list())<5) { $j++; threads->new(\&ss,$j,$j); } foreach $thread(threads->list(threads::all)) { if($thread->is_joinable()) { $thread->join(); print scalar(threads->list()),"\t$j\t",localtime(time),"\n"; } } } foreach $thread(threads->list(threads::all)) { $thread->join();print scalar(threads->list()),"\t$j\t",localtime(time),"\n"; } print localtime(time),"\n"; sub ss() { my ($t,$s)=@_; sleep($t); print "$s\t"; }
上述方法有一個極大的缺陷。如果正在執行的五個線程都沒有執行完成,最外層的while循環將會一直“空轉”,直到有一個線程被join掉,while循環在控制創建新的線程。這個過程中,主線程因為這個while循環的存在,會一直耗費系統資源。其實在任務管理器中可以看到,我們的程序會耗費50%的CPU(雙核CPU),實際上這都耗費在了沒有執行任何功能的外層while循環上。
在perl創建的線程結束時不會有任何提示,以告訴主線程說自己(從線程)已經結束。所以必須使用附加程序來判斷目標線程是否已經執行完成,并立即join掉線程,以釋放系統資源。但是這個判斷過程及耗系統資源。正如上面的程序。
為此,在一次google了一下,感謝云舒提供的方法(http://icylife.net/yunshu/show.php?id=617),終于學會了。
---------------------------------------------------
信號量
Thread::Semaphore 包為線程提供了信號量的支持。使用信號量可以控制并行的線程數。
對象申明:
my $semaphore = Thread::Semaphore->new(5);
or my $semaphore = new Thread::Semaphore(5); 控制并行的最大線程數
對象方法:
$semaphore->down;
獲取一個信號量,之后可用的信號量減1,當$semaphore=0時,表示所有的線程都已經創建,無法獲得新的信號量,并且此時主線程處于等待。直到獲得新的信號量為止。
$semaphore->up;
釋放一個信號量,之后可用信號量加1.當一個線程執行完畢時,調用此方法,釋放一個信號量。此時 $semaphore->down方法獲得信號量成功。處于等待的主線程從新喚醒,創建新的線程。
--------------------------------------------------------------------
收割線程
上次使用的程序收割線程時使用的是join方法,join方法的特點就是如果線程已經執行完畢,那么此時調用join方法理所當然,但是如果join調用過早,線程還沒有執行完畢,主線程就會處于擁堵狀態。知道從線程執行完畢,join方法完成為止。這極大的限制了程序運行的性能。
perl里面提供了另外一種收割線程的方法:detach。detach的特點是直接把從線程從主線程里面剝離,以后互不相關。當從線程執行完畢時會自動釋放占有的資源。算是省心省力了。這里我們將嘗試使用detach方法收割線程,并且使用信號量來控制線程的數量。
實例:
------------------------------------------------------------
use threads; use Thread::Semaphore; my $j=0; my $thread;my $max_threads=5; my $semaphore=new Thread::Semaphore($max_threads); print localtime(time),"\n"; while() { if($j>10) { last; } $j=$j+1; #獲得一個信號量;當執行的線程數為5時,獲取失敗,主線程等待。直到有一個線程結束,新的信號量可用。回復正常運行; $semaphore->down(); my $thread=threads->new(\&ss,$j,$j); #創建線程; $thread->detach(); #剝離線程; } #必須存在的部分,用在控制在主線程結束前保證所有剝離的線程已經執行完成。否則的話,當主線程結束時還有從線程沒有執行完成,這時從線程將不得不被強行kill掉(皮之不存毛將焉附)。 &waitquit; print localtime(time),"\n"; sub ss() { my ($t,$s)=@_; sleep($t); print "$s\t",scalar(threads->list()),"\t$j\t",localtime(time),"\n"; $semaphore->up(); #當線程執行完成時,釋放信號量。 } #來源于云舒(http://icylife.net/yunshu/show.php?id=617); sub waitquit { print "Waiting to quit...\n"; my $num=0; while($num<$max_thread) { $semaphore->down(); $num++; print "$num thread quit...\n"; } print "All $max_thread thread quit\n"; }
------------------------------------------------
程序運行結果和前文一致。就不描述了(http://blog.sina.com.cn/s/blog_7ea1e7fc0100r61x.html)。
與上次使用的代碼最大區別在于少了一個外循環用來判斷從線程是否已經執行完成。極大的降低了CPU的使用率。
以上是“perl多線程的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。