深入剖析Perl線程的生命周期
本文和大家繼續(xù)討論一下Perl線程的生命周期,主要包括共享與同步,鎖,信號量和Perl線程隊列幾部分內(nèi)容,相信通過本文的學(xué)習(xí)你對Perl線程生命周期的概念有一定的了解。
共享與同步
threads::shared
和現(xiàn)有大多數(shù)Perl線程模型不同,在PerlithreadsPerl線程模型中,默認情況下任何數(shù)據(jù)結(jié)構(gòu)都不是共享的。當一個新Perl線程被創(chuàng)建以后,它就已經(jīng)包含了當前所有數(shù)據(jù)結(jié)構(gòu)的一份私有拷貝,新建Perl線程中對這份拷貝的數(shù)據(jù)結(jié)構(gòu)的任何操作都不會在其他Perl線程中有效。因此,如果需要使用任何共享的數(shù)據(jù),都必須顯式地申明。threads::shared包可以用來實現(xiàn)Perl線程間共享數(shù)據(jù)的目的。
鎖
多Perl線程間既然有了共享的數(shù)據(jù),那么就必須對共享數(shù)據(jù)進行小心地訪問,否則,沖突在所難免。PerlithreadsPerl線程模型中內(nèi)置的lock方法實現(xiàn)了Perl線程間共享數(shù)據(jù)的鎖機制。有趣的是,并不存在一個unlock方法用來顯式地解鎖,鎖的生命周期以代碼塊為單位,也就是說,當lock操作所在的代碼塊執(zhí)行結(jié)束之后,也就是鎖被隱式釋放之時。例如
清單10.Perl線程中的鎖機制
- usethreads::shared;
- #inthread1
- {
- lock($share);#lockfor3seconds
- sleep(3);#otherthreadscannotlockagain
- }
- #unlockimplicitlynowaftertheblock
- #inthread2
- {
- lock($share);#willbeblocked,asalreadylockedbythread1
- $share++;#afterthread1quitfromtheblock
- }
- #unlockimplicitlynowaftertheblock
上面的示例中,我們在thread1中使用lock方法鎖住了一個普通的標量,這會導(dǎo)致thread2在試圖獲取$share變量的鎖時被阻塞,當thread1從調(diào)用lock的代碼塊中退出時,鎖被隱式地釋放,從而thread2阻塞結(jié)束,lock成功以后,thread2才可以執(zhí)行$share++的操作。對于數(shù)組和哈希表來說,lock必須用在整個數(shù)據(jù)結(jié)構(gòu)上,而不是用在數(shù)組或哈希表的某一個元素上。
假如一個Perl線程對某一個共享變量實施了鎖操作,在它沒有釋放鎖之前,如果另外一個Perl線程也對這個共享變量實施鎖操作,那么這個Perl線程就會被阻塞,阻塞不會被自動中止而是直到前一個Perl線程將鎖釋放為止。這樣的模式就帶來了我們常見的死鎖問題。
例如
清單12.Perl線程中的死鎖
- usethreads;
- usethreads::shared;
- #inthread1
- {
- lock($a);#lockfor3seconds
- sleep(3);#otherthreadscannotlockagain
- lock($b);#deadlockhere
- }
- #inthread2
- {
- lock($b);#willbeblocked,asalreadylockedbythread1
- sleep(3);#afterthread1quitfromtheblock
- lock($a);#deadlockhere
- }
死鎖常常是多Perl線程程序中最隱蔽的問題,往往難以發(fā)現(xiàn)與調(diào)試,也增加了排查問題的難度。為了避免在程序中死鎖的問題,在程序中我們應(yīng)該盡量避免同時獲取多個共享變量的鎖,如果無法避免,那么一是要盡量使用相同的順序來獲取多個共享變量的鎖,另外也要盡可能地細化上鎖的粒度,減少上鎖的時間。#p#
信號量
Thread::Semaphore包為Perl線程提供了信號量的支持。你可以創(chuàng)建一個自己的信號量,并通過down操作和up操作來實現(xiàn)對資源的同步訪問。實際上,down操作和up操作對應(yīng)的就是我們所熟知的P操作和V操作。從內(nèi)部實現(xiàn)上看,Thread::Semaphore本質(zhì)上就是加了鎖的共享變量,無非是把這個加了鎖的共享變量封裝成了一個Perl線程安全的包而已。由于信號量不必與任何變量綁定,因此,它非常靈活,可以用來控制你想同步的任何數(shù)據(jù)結(jié)構(gòu)和程序行為。例如
清單13.Perl線程中的信號量
- usethreads;
- usethreads::shared;
- useThread::Semaphore;
- my$s=Thread::Semaphore->new();
- $s->down();#Poperation
- ...
- $s->up();#Voperation
從本質(zhì)上說,信號量是一個共享的整型變量的引用。默認情況下,它的初始值為1,down操作使它的值減1,up操作使它的值加1。當然,你也可以自定義信號量初始值和每次up或down操作時信號量的變化。
Perl線程隊列
Thread::Queue包為Perl線程提供了Perl線程安全的隊列支持。與信號量類似,從內(nèi)部實現(xiàn)上看,Thread::Queue也是把一個通過鎖機制實現(xiàn)同步訪問的共享隊列封裝成了一個Perl線程安全的包,并提供統(tǒng)一的使用接口。Thread::Queue在某些情況下可以大大簡化Perl線程間通信的難度和成本。例如在生產(chǎn)者-消費者模型中,生產(chǎn)者可以不斷地在Perl線程隊列上做enqueue操作,而消費者只需要不斷地在Perl線程隊列上做dequeue操作,這就很簡單地實現(xiàn)了生產(chǎn)者和消費者之間同步的問題。
其他有用的非核心包
本文前面討論的所有內(nèi)容都在Perl線程核心包的范疇之內(nèi)。其實CPAN上還有其他一些與Perl線程相關(guān)的非核心包,它們往往也會給Perl線程的使用帶來很大的便利,這里我們選出兩個稍加介紹,拋磚引玉。
Thread::Pool包允許你在程序中創(chuàng)建一批Perl線程去完成多個類似的任務(wù)。例如當你希望創(chuàng)建一個多Perl線程程序去完成檢驗1000個ip地址是否都能ping通的任務(wù)時,Thread::Pool包可以給你帶來便利。
Thread::RWLock包為Perl線程中的讀寫操作提供了鎖機制的支持。例如當你有多個reader和writerPerl線程共同訪問某一個或幾個文件時,Thread::RWLock包可以給你帶來便利。
總結(jié)
本文主要介紹了Perl中Perl線程的使用方法,包括Perl線程的創(chuàng)建、執(zhí)行與消亡,如何在Perl線程中使用共享變量并通過鎖機制、信號量和Perl線程隊列的方法來實現(xiàn)Perl線程間的同步。PerlithreadsPerl線程模型與主流Perl線程模型***的不同之處在于默認情況下任何數(shù)據(jù)結(jié)構(gòu)都是非共享的,或者說Perl中的ithreads是一個“非輕量級”的Perl線程模型。雖然這樣的Perl線程模型增加了程序的開銷,但它并不會在Perl線程的功能性上打折扣,同時它也使得Perl線程間的通訊和共享變得更加簡單。這也符合了Perl一貫的簡單而強大的理念和原則。
【編輯推薦】




















