2010-11-01 75 views
2

我正在編寫一個應用程序,用於偵聽聲音事件(使用傳入的Open Sound Control消息),然後基於這些事件暫停或恢復程序執行。我的結構大部分時間工作,但總是在主循環中爆炸,所以我猜這是一個線程問題。下面是我在談論的一個通用的,簡化的版本:理想的線程結構問題(涉及多線程通信)

import time, threading 

class Loop(): 
    aborted = False 

    def __init__(self): 
     message = threading.Thread(target=self.message, args=((0),)) 
     message.start() 

     loop = threading.Thread(target=self.loop) 
     loop.start() 


    def message(self,val): 

     if val > 1: 
      if not self.aborted: 
       self.aborted = True 
       # do some socket communication    
      else: 
       self.aborted = False 
       # do some socket communication 


    def loop(self): 
     cnt = 0 

     while True: 
      print cnt 
      if self.aborted: 
       while self.aborted: 
        print "waiting" 
        time.sleep(.1); 
      cnt += 1 


class FakeListener(): 
    def __init__(self,loop): 
     self.loop = loop 
     listener = threading.Thread(target=self.listener) 
     listener.start() 

    def listener(self): 
     while True: 
      loop.message(2) 
      time.sleep(1) 



if __name__ == '__main__': 

    loop = Loop() 

    #fake listener standing in for the real OSC event listener 
    listener = FakeListener(loop) 

當然,這個簡單的代碼似乎工作的偉大,所以這顯然不能完全說明我真正的代碼,但你的想法。這裏沒有包含的事實是,在每個循環中暫停和恢復(通過設置aborted = True/False)會導致某些套接字通信,這也涉及到線程。

在我的代碼中經常發生的事情是,主循環並不總是在聲音事件發生後停止。它可以用於很多事件,但最終它不會回答。

有關如何在線程之間構建這種通信的任何建議?

更新:

好吧,我想我已經明白了。這是一個似乎可行的修改。有一個監聽器線程可以定期將值放入Queue對象中。有一個檢查器線程會不斷檢查隊列以查找值,一旦它看到它將布爾值設置爲其相反的狀態。該布爾值控制循環線程是繼續還是等待。

雖然我並不完全確定q.task_done()函數在做什麼。

import time, threading 
import Queue 

q = Queue.Queue(maxsize = 0) 

class Loop(): 
    aborted = False 

    def __init__(self): 
     checker = threading.Thread(target=self.checker) 
     checker.setDaemon(True) 
     checker.start() 

     loop = threading.Thread(target=self.loop) 
     loop.start() 


    def checker(self): 
     while True: 
      if q.get() == 2: 
       q.task_done() 
       if not self.aborted: 
        self.aborted = True 
       else: 
        self.aborted = False 


    def loop(self): 
     cnt = 0 

     while cnt < 40: 
      if self.aborted: 
       while self.aborted: 
        print "waiting" 
        time.sleep(.1) 
      print cnt 
      cnt += 1 
      time.sleep(.1) 


class fakeListener(): 

    def __init__(self): 
     listener = threading.Thread(target=self.listener) 
     listener.setDaemon(True) 
     listener.start() 

    def listener(self): 
     while True: 
      q.put(2) 
      time.sleep(1) 



if __name__ == '__main__': 

    #fake listener standing in for the real OSC event listener 
    listener = fakeListener() 

    loop = Loop() 
+2

這是您的示例重寫爲使用多處理http://codepad.org/IRz04lkH – jfs 2010-11-06 10:10:02

+0

多線程庫相比線程有什麼好處?我通常使用i5在Mac上運行,因此有多個內核可以使用。但是如果它在沒有多核的機器上運行,它是否默認爲典型的線程實現? – mix 2010-11-06 20:49:28

+0

對於微不足道的例子,這取決於你的真實程序的功能。 Python擁有GIL,它可以訪問解釋器的大部分內容。 Mindblowing Python GIL http://blip.tv/file/2232410 – jfs 2010-11-07 07:56:54

回答

4

嗯..我不完全理解你的問題,但我會盡我所能解釋我認爲你需要解決你的問題。

1)你的Loop.loop函數的線程應該被設置爲一個守護進程線程,以便它和主線程一起退出(所以你不必在每次關閉你的程序時終止python進程)。要做到這一點,只需在調用線程的「開始」函數之前先放置loop.setDaemon(True)即可​​。

2)線程之間進行通信的最簡單且不能通過的方式是使用Queue。在線程會將一個項目放入該隊列中,另一個線程將取出一個項目,對該項目執行某些操作然後終止(或獲得另一個任務)

在Python中,隊列可以是從全局列表到Python的內置任何內容在隊列對象。我推薦python隊列,因爲它是線程安全和易於使用的。

+0

如果一個線程將一個項目放入隊列中,另一個線程知道如何獲取它?第二個線程是否需要定期檢查它以尋找新的項目?thx爲守護進程提示。 – mix 2010-11-05 20:37:55

+0

+1 [隊列](http://docs.python.org/library/queue.html)對Python中的消息傳遞非常棒(如果你喜歡自己做這些東西的話)。它支持阻塞和非阻塞操作。 – 2010-11-06 06:28:45

+1

@mix:你只需要在你的線程中調用'queue.get()',並且默認情況下它會無限期地被阻塞(直到一個項目可用)。 – jfs 2010-11-06 06:50:04