1

之前,此問題是由以下問題跟進:With statement and python threading

我一直在使用Python線程API試驗。 我有這樣的代碼,它適用於我想要實現的功能:---->在調用python線程運行之前執行函數執行。但是要做到這一點,我總是不得不在run()方法中調用time.sleep(1)以使其繼續執行(),否則線程將退出而不進行函數分配和執行。是否有更好的方法實現這種等待?隨着句和線程:讓函數執行運行

from __future__ import print_function 
import threading 
import time 
import functools 
import contextlib 
import thread 
from threading import Lock 
#import contextlib 
#Thread module for dealing with lower level thread operations.Thread is limited use Threading instead. 

def timeit(fn): 
    '''Timeit function like this doesnot work with the thread calls''' 
    def wrapper(*args,**kwargs): 
     start = time.time() 
     fn(*args,**kwargs) 
     end = time.time() 
     threadID = "" 
     print ("Duration for func %s :%d\n"%(fn.__name__ +"_"+ threading.current_thread().name ,end-start)) 
    return wrapper 

exitFlag = 0 

@timeit 
def print_time(counter,delay): 
    while counter: 
     if exitFlag: 
      thread.exit() 
     time.sleep(delay) 
     print("%s : %s_%d"%(threading.current_thread().name,time.ctime(time.time()),counter)) 
     counter -= 1 

class Mythread(threading.Thread): 
    def __init__(self,threadID,name): 
     threading.Thread.__init__(self) 
     self.threadID = threadID 
     self.name = name 
     self._f = None 

    def run(self): 
     print("Starting%s\n" % self.name) 
     time.sleep(1) 
     if self._f: 
      self._f() 
      print("Exiting%s\n" % self.name) 
     else: 
      print("Exiting%s without function execution\n" % self.name) 

#  def set_f(self,f): 
#   self._f = f 

    def execute(self,f,*args,**kwargs): 
     self._f=functools.partial(f,*args,**kwargs) 

    def __enter__(self): 
     self.start() 

    def __exit__(self,type,value,traceback): 
     self.join() 




class ThreadContainer(object): 
    def __init__(self,id,name): 
     self._t = Mythread(id,name) 

    def execute(self,f,*args,**kwargs): 
     self._f=functools.partial(f,*args,**kwargs) 
     self._t.set_f(self._f) 
#  self._t.start() 
#   self._t.join() 


    def __enter__(self): 
     self._t.start() 

    def __exit__(self,type,value,traceback): 
     self._t.join() 




if __name__ == '__main__': 
    ''' 
    print_time(5, 1) 
    threadLock = threading.Lock() 
    threads = [] 
    thread1 = Mythread(1,"Thread1",5,1) 
    thread2 = Mythread(2,"Thread2",5,2) 
    thread1.start() 
    thread2.start() 
    threads.append(thread1) 
    threads.append(thread2) 
    for t in threads: 
     t.join() 
    ''' 
#  thread1 = Mythread(1,"Thread1") 
#  thread2 = Mythread(2,"Thread2") 
#  with contextlib.nested(ThreadContainer(1,"Thread1"),ThreadContainer(2,"Thread2")) as (t1,t2): 
#   t1.execute(print_time,5,1) 
#   t2.execute(print_time,5,2) 
    t1 = Mythread(1,"Thread1") 
    t2 = Mythread(2,"Thread2") 
    with contextlib.nested(t1,t2): 
     t1.execute(print_time,5,1) 
     t2.execute(print_time,5,2) 


    print("Exiting main thread ") 
+0

如果你剝離它們,你的問題會更容易回答 - 刪除註釋掉的代碼,你永遠不會實例化的類,等等,如果它們與這個問題無關,並且提供一個最小的例子來展示你所問​​的東西(理想的情況是一個不需要滾動讀取)。 – abarnert 2013-05-07 18:02:19

回答

1

這裏的問題是,你希望run功能要等到execute函數被調用。

當然明顯的解決方法是調用execute打電話之前start

t1.execute(print_time,5,1) 
t2.execute(print_time,5,2) 
with contextlib.nested(t1, t2): 
    pass 

...或只是讓execute電話start,或傳遞函數的構造函數或start呼叫,或...

此外,你的設計有點奇怪。線程函數被設計用於處理_f尚未設置的情況......但您希望它等到_f已被設置?


但是這是可以想象的,這種問題可以想出一個更現實的設計,所以,讓我們來看看如何解決這個問題。

首先,添加sleep來解決線程問題幾乎總是表明您正在做的事情非常錯誤。這也是導致可怕性能問題的好方法(例如:在足夠多的地方添加足夠的sleep s以使所有內容都工作正常,需要30秒才能啓動應用程序,而不是30毫秒) - 更糟糕的是, ,比賽條件錯誤(確保1秒總是足夠的時間,對嗎?除非計算機正在進行交換,或從休眠中醒來,或者忙於其他使用所有CPU的程序,或...)。

如果您嘗試跨線程同步操作,則需要使用同步對象。訣竅是知道正確的。通過Event閱讀Lock的文檔(和3.x增加了Barrier),並找到在一般線程得到一個什麼樣所有這些事情是更廣闊的想法的教程。*

在這種情況下,你已經得到的代碼正在等待一些更改以保存狀態,而其他代碼正在進行更改,這是'Condition'的典型用例。所以:

class Mythread(threading.Thread): 
    def __init__(self, threadID, name, condition): 
     self.condition = condition 
     # ... same as before 

    def run(self): 
     # ... setup before checking for _f 

     with self.condition: 
      while not self._f: 
       self.condition.wait() 
     self._f() 

     # ... anything else you want 

現在,你需要創建Condition,它傳遞給線程,notify它。

你可以使用一個單一的Condition

condition = threading.Condition() 
t1 = Mythread(1, "Thread1", condition) 
t2 = Mythread(2, "Thread2", condition) 
with contextlib.nested(t1,t2): 
    with condition: 
     t1.execute(print_time, 5, 1) 
     t2.execute(print_time, 5, 2) 
     condition.notify_all() 

或者,你可以爲每個線程提供自己的Condition

class Mythread(threading.Thread): 
    def __init__(self, threadID, name): 
     self.condition = Condition() 
     # ... same as before 

# ... 

t1 = Mythread(1, "Thread1") 
t2 = Mythread(2, "Thread2") 
with contextlib.nested(t1,t2): 
    with t1.condition: 
     t1.execute(print_time, 5, 1) 
     t1.condition.notify() 
    with t2.condition: 
     t2.execute(print_time, 5, 1) 
     t2.condition.notify() 

請注意,這並不讓你以明確的「未設置」_f,但這很容易做到這一點。例如,您可以添加_f_set屬性,並檢查該屬性,而不是_f,因此有人可以撥打execute(None)(然後notify)將您喚醒並轉到「否_f」的情況。


*警告:某些命名不一致。還有一種叫做「屏障」的不同的東西,另一種不同的東西也被稱爲「屏障」,並且有許多與「蟒蛇」不同的「事件」變體(其中一些更像是一種條件,但不是實際上可以這樣使用),有時一個「條件變量」是由同步對象而不是同步對象保護的實際共享狀態,依此類推...