2011-07-07 83 views
2

我有一個線程化的Python應用程序,在後臺線程中有一個長時間運行的主循環。此背景主循環實際上是對pyglet.app.run()的調用,它驅動GUI窗口,並且可以配置爲定期調用其他代碼。我需要從主線程中隨意調用do_stuff(duration)函數來觸發GUI中的動畫,等待動畫停止,然後返回。實際的動畫必須在後臺線程中完成,因爲GUI庫無法處理被單獨的線程驅動。阻止主線程,直到python後臺線程完成側任務

我相信我需要做這樣的事情:

import threading 

class StuffDoer(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 
     self.max_n_times = 0 
     self.total_n_times = 0 
     self.paused_ev = threading.Event() 

    def run(self): 
     # this part is outside of my control 
     while True: 
      self._do_stuff() 
      # do other stuff 

    def _do_stuff(self): 
     # this part is under my control 
     if self.paused_ev.is_set(): 
      if self.max_n_times > self.total_n_times: 
       self.paused_ev.clear() 
     else: 
      if self.total_n_times >= self.max_n_times: 
       self.paused_ev.set() 
     if not self.paused_ev.is_set(): 
      # do stuff that must execute in the background thread 
      self.total_n_times += 1 

sd = StuffDoer() 
sd.start() 

def do_stuff(n_times): 
    sd.max_n_times += n_times 
    sd.paused_ev.wait_for_clear() # wait_for_clear() does not exist 
    sd.paused_ev.wait() 
    assert (sd.total_n_times == sd.max_n_times) 

編輯:使用max_n_times而不是stop_time解釋,爲什麼Thread.join(duration)不會做的伎倆。

從文檔threading.Event

等待([超時])

塊直到內部標誌爲真。 如果內部標誌在輸入時爲真,則立即返回 。否則,阻止 ,直到另一個線程調用set()爲 將該標誌設置爲true,或者直到發生可選超時 。

我發現我可以得到我要找的,如果我有一雙事件,paused_evnot_paused_ev的行爲,並使用not_paused_ev.wait()。我幾乎可以使用Thread.join(duration),除了它只需要在後臺線程實際上註冊時間結束時才精確返回。是否有其他同步對象或其他策略我應該使用?

我也願意接受這樣一個觀點,即我以錯誤的方式接近整個事情,只要他們是很好的論點。

+0

您能否澄清一下您試圖完成的任務?當主線程終止時你想要發生什麼?或者你想要在主線程運行的時間內發生一段特定的時間?或者主線程需要發信號給另一個線程?你的目標是什麼? –

+0

對不起,是的,增加了更多的說明。我不想給太多關於GUI的細節,因爲我的問題主要是關於線程和阻塞。 –

+0

我發現並解決了一個非常重要的問題(非常抱歉,很難測試未寫入的代碼)。我需要等待的是'paused_ev'的一個上升沿,它寫入的方式正在等待一個_rising_邊緣。 –

回答

1

我結束了使用類似於@wberry提出一個隊列,使得Queue.task_doneQueue.wait使用:

import Queue 
import threading 

class StuffDoer(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 
     self.setDaemon(True) 
     self.max_n_times = 0 
     self.total_n_times = 0 
     self.do_queue = Queue.Queue() 

    def run(self): 
     # this part is outside of my control 
     while True: 
      self._do_stuff() 
      # do other stuff 

    def _do_stuff(self): 
     # this part is under my control 
     if self.total_n_times >= self.max_n_times: 
      try: 
       self.max_n_times += self.do_queue.get(block=False) 
      except Queue.Empty, e: 
       pass 
     if self.max_n_times > self.total_n_times: 
      # do stuff that must execute in the background thread 
      self.total_n_times += 1 
      if self.total_n_times >= self.max_n_times: 
       self.do_queue.task_done() 

sd = StuffDoer() 
sd.start() 

def do_stuff(n_times): 
    sd.do_queue.put(n_times) 
    sd.do_queue.join() 
    assert (sd.total_n_times == sd.max_n_times) 
3

希望我從我的評論中得到一些修訂或額外的信息,但我有點想知道你是不是通過繼承Thread來過度使用事物。你可以做這樣的事情:

class MyWorker(object): 
    def __init__(self): 
    t = Thread(target = self._do_work, name "Worker Owned Thread") 

    t.daemon = True 

    t.start() 

    def _do_work(self): 
    While True: 
     # Something going on here, forever if necessary. This thread 
     # will go away if the other non-daemon threads terminate, possibly 
     # raising an exception depending this function's body. 

我覺得這更有意義,當你要運行的方法是什麼,是更恰當一些其他類的成員函數比這將是作爲run方法線程。另外,這可以讓你不必在Thread中封裝一堆業務邏輯。當然,所有IMO。

+0

我認爲這對我來說不會是一種選擇,因爲工作必須發生在正在進行其他工作的特定的,已經運行的線程中。 –

1

看起來您的GUI動畫線程正在其while True循環中使用自旋鎖。這可以使用線程安全隊列來防止。根據我對你的問題的閱讀,這種方法在功能上是等效的和高效的。

我省略了一些不會改變的代碼細節。我在這裏也假設你不控制的run()方法使用self.stop_time值來完成它的工作;否則不需要線程安全隊列。

from Queue import Queue 
from threading import Event 

class StuffDoer: 
    def __init__(self, inq, ready): 
    self.inq = inq 
    self.ready = ready 
    def _do_stuff(self): 
    self.ready.set() 
    self.stop_time = self.inq.get() 

GUIqueue = Queue() 
control = Event() 

sd = StuffDoer(GUIqueue, control) 

def do_stuff(duration): 
    control.clear() 
    GUIqueue.put(time.time() + duration) 
    control.wait() 
0

我做了基於@g.d.d.cadvice這個問題的解決方案。這裏是我的代碼:

threads = [] 
# initializing aux thread(s) in the main thread ... 
t = threading.Thread(target=ThreadF, args=(...)) 
#t.setDaemon(True) # I'm not sure does it really needed 
t.start() 
threads.append(t.ident) 

# Block main thread 
while filter(lambda thread: thread.ident in threads, threading.enumerate()): 
    time.sleep(10) 

此外,您還可以使用Thread.join阻止主線程 - 這是更好的辦法。

相關問題