2016-03-01 10 views
1

我有一個主線程的Python程序,讓我們說2個其他線程(或許更多,可能無所謂)。我想讓主線程休眠直到其他線程中的一個完成。使用輪詢很容易(通過調用t.join(1)併爲每個線程t等待一秒鐘)。加入Python中的許多線程中的一個

是否有可能做沒有投票,只是

SOMETHING_LIKE_JOIN(1, [t1, t2]) 

其中t1和t2 threading.Thread對象?該呼叫必須執行以下操作:睡眠1秒鐘,但在t1,t2中的一個完成後立即喚醒。與POSIX select(2)與兩個文件描述符的調用非常相似。

+1

也許條件對象?主線程等待條件對象。 t1和t2在返回之前調用condition_obj.notify()。返回的線程首先喚醒主線程。這裏是條件對象的文檔:[鏈接](https://docs.python.org/3/library/threading.html#condition-objects) –

+0

我在這裏看到競爭條件:想象t1完成並稱爲「通知」 ,主線程醒來,做一些東西,並想再次入睡(等待t2,t3,t4等)。可能發生的情況是,在主線程中「做某些事情」時,有些甚至全部t2,... t4線程將完成其工作並調用「通知」。然後主線程會叫「等待」並永遠睡眠,因爲沒有人再喚醒它。 – ilya

+1

沒有比賽條件。條件對象包含一個RLock。我稍後會發佈一個例子。 –

回答

-1

您可以創建一個線程類,並且主線程保持對它的引用。所以你可以檢查線程是否完成,並讓你的主線程輕鬆地繼續。

如果那對你沒有幫助,我建議你看看隊列庫!

import threading 
import time, random 


#THREAD CLASS# 
class Thread(threading.Thread): 

    def __init__(self): 
     threading.Thread.__init__(self) 

     self.daemon = True 
     self.state = False 

     #START THREAD (THE RUN METHODE)# 
     self.start() 

    #THAT IS WHAT THE THREAD ACTUALLY DOES# 
    def run(self): 

     #THREAD SLEEPS FOR A RANDOM TIME RANGE# 
     time.sleep(random.randrange(5, 10)) 

     #AFTERWARDS IS HAS FINISHED (STORE IN VARIABLE)# 
     self.state = True 


    #RETURNS THE STATE# 
    def getState(self): 

     return self.state 


#10 SEPERATE THREADS# 
threads = [] 

for i in range(10): 
    threads.append(Thread()) 

#MAIN THREAD# 
while True: 

    #RUN THROUGH ALL THREADS AND CHECK FOR ITS STATE# 
    for i in range(len(threads)): 
     if threads[i].getState(): 
      print "WAITING IS OVER: THREAD ", i 

    #SLEEPS ONE SECOND# 
    time.sleep(1) 
+0

不,它不會這樣做:問題是關於「不投票」的解決方案,即無法調用「睡眠(1)」。 – ilya

1

以下是使用條件對象的示例。

from threading import Thread, Condition, Lock 
from time import sleep 
from random import random 


_lock = Lock() 


def run(idx, condition): 
    sleep(random() * 3) 
    print('thread_%d is waiting for notifying main thread.' % idx) 
    _lock.acquire() 
    with condition: 
     print('thread_%d notifies main thread.' % idx) 
     condition.notify() 


def is_working(thread_list): 
    for t in thread_list: 
     if t.is_alive(): 
      return True 
    return False 


def main(): 
    condition = Condition(Lock()) 
    thread_list = [Thread(target=run, kwargs={'idx': i, 'condition': condition}) for i in range(10)] 

    with condition: 
     with _lock: 
      for t in thread_list: 
       t.start() 

      while is_working(thread_list): 
       _lock.release() 
       if condition.wait(timeout=1): 
        print('do something') 
        sleep(1) # <-- Main thread is doing something. 
       else: 
        print('timeout') 

    for t in thread_list: 
     t.join() 


if __name__ == '__main__': 
    main() 

我不認爲你有評論中描述的競爭條件。條件對象包含一個Lock。當主線程正在工作(例子中爲sleep(1))時,它保存該鎖,並且沒有線程可以通知它,直到它完成工作並釋放鎖。


我剛剛意識到在前面的例子中存在競爭條件。我添加了一個全局_lock來確保條件從不通知主線程,直到主線程開始等待。我不喜歡它是如何工作的,但我還沒有想出更好的解決方案...

+1

請使用具有鎖定,條件等的'with'語句避免忘記'release'的風險,並防止意外繞過'release'.Plus,它更短,最後四行'run'變成:''條件:','condition.notify()','print('thread_%d'%idx)' – ShadowRanger

+1

謝謝你的建議。我已編輯run(),並在main() –

+0

謝謝。上投了反對票。 – ShadowRanger

2

一個解決方案是使用multiprocessing.dummy.Pool; multiprocessing.dummy提供的API幾乎與multiprocessing相同,但由線程支持,因此它爲您提供免費的線程池。

例如,你可以這樣做:

from multiprocessing.dummy import Pool as ThreadPool 

pool = ThreadPool(2) # Two workers 
for res in pool.imap_unordered(some_func, list_of_func_args): 
    # res is whatever some_func returned 

multiprocessing.Pool.imap_unordered返回結果變得可用,無論哪個任務首先完成。

如果你可以使用Python 3.2或更高版本(或安裝concurrent.futures的PyPI模塊老的Python),您可以通過從ThreadPoolExecutor創建一個或多個Future S,然後使用concurrent.futures.waitreturn_when=FIRST_COMPLETED,或使用concurrent.futures.as_completed推廣到不同的任務功能類似的效果。