2012-09-07 14 views
15

यदि मेरे पास दो threading.Event() ऑब्जेक्ट्स हैं, और उनमें से कोई भी सेट होने तक सोना चाहता है, क्या पाइथन में ऐसा करने का कोई प्रभावी तरीका है? स्पष्ट रूप से मैं मतदान/टाइमआउट के साथ कुछ कर सकता था, लेकिन जब तक कि कोई सेट नहीं किया जाता है, तब तक मैं वास्तव में थ्रेड नींद लेना चाहता हूं, select फ़ाइल डिस्क्रिप्टर के लिए कैसे उपयोग किया जाता है।पायथन थ्रेडिंग: क्या मैं दो थ्रेडिंग पर सो सकता हूं। इवेंट() एक साथ?

तो निम्नलिखित कार्यान्वयन में, wait_for_either का एक कुशल गैर-मतदान कार्यान्वयन क्या होगा?

a = threading.Event() 
b = threading.Event() 

wait_for_either(a, b) 
+0

क्या 2 अलग-अलग आयोजनों का उपयोग करने के लिए कोई अच्छा कारण है और इसका उपयोग नहीं किया जाता है? –

+0

@ यूलियस आपके पास एक सिंगल थ्रेड है जिसे आप ईवेंट संचालित करना चाहते हैं, लेकिन इसमें 2 कतार हैं ... इसलिए जब आपको कोई आइटम प्राप्त होता है तो आपको जागने की आवश्यकता होती है – pyInTheSky

+0

मुझे आश्चर्य है कि पाइथन में यह अंतर्निहित नहीं है। –

उत्तर

18

यहाँ एक गैर मतदान गैर अत्यधिक धागा समाधान है: जब भी वे बदलने के एक कॉलबैक आग मौजूदा Event रों संशोधित, और कहा कि कॉलबैक में एक नई घटना की स्थापना संभाल:

import threading 

def or_set(self): 
    self._set() 
    self.changed() 

def or_clear(self): 
    self._clear() 
    self.changed() 

def orify(e, changed_callback): 
    e._set = e.set 
    e._clear = e.clear 
    e.changed = changed_callback 
    e.set = lambda: or_set(e) 
    e.clear = lambda: or_clear(e) 

def OrEvent(*events): 
    or_event = threading.Event() 
    def changed(): 
     bools = [e.is_set() for e in events] 
     if any(bools): 
      or_event.set() 
     else: 
      or_event.clear() 
    for e in events: 
     orify(e, changed) 
    changed() 
    return or_event 

नमूना उपयोग:

def wait_on(name, e): 
    print "Waiting on %s..." % (name,) 
    e.wait() 
    print "%s fired!" % (name,) 

def test(): 
    import time 

    e1 = threading.Event() 
    e2 = threading.Event() 

    or_e = OrEvent(e1, e2) 

    threading.Thread(target=wait_on, args=('e1', e1)).start() 
    time.sleep(0.05) 
    threading.Thread(target=wait_on, args=('e2', e2)).start() 
    time.sleep(0.05) 
    threading.Thread(target=wait_on, args=('or_e', or_e)).start() 
    time.sleep(0.05) 

    print "Firing e1 in 2 seconds..." 
    time.sleep(2) 
    e1.set() 
    time.sleep(0.05) 

    print "Firing e2 in 2 seconds..." 
    time.sleep(2) 
    e2.set() 
    time.sleep(0.05) 

जिसके परिणामस्वरूप था:

Waiting on e1... 
Waiting on e2... 
Waiting on or_e... 
Firing e1 in 2 seconds... 
e1 fired!or_e fired! 

Firing e2 in 2 seconds... 
e2 fired! 

गु थ्रेड-सुरक्षित होना चाहिए। किसी भी टिप्पणी का स्वागत है।

संपादित करें: ओह और यहां आपका wait_for_either फ़ंक्शन है, हालांकि जिस तरह से मैंने कोड लिखा था, यह or_event के आसपास बनाना और पास करना सबसे अच्छा है। ध्यान दें कि or_event मैन्युअल रूप से सेट या साफ़ नहीं किया जाना चाहिए।

def wait_for_either(e1, e2): 
    OrEvent(e1, e2).wait() 
+2

यह अच्छा है! हालांकि, मुझे एक समस्या दिखाई देती है: यदि आप दो बार एक ही घटना को 'सूचित' करते हैं, तो जब भी आप इसे सेट या साफ़ करते हैं तो आपको एक अनंत लूप मिलेगा। – Vincent

+0

यह एक अच्छा मुद्दा है! जल्द ही – Claudiu

+0

संशोधित करेगा इसके लिए बहुत बहुत धन्यवाद! यह वही है जो मैं खोज रहा था। क्या आप इस उत्तर में कोड को ओपन सोर्स लाइसेंसिंग शर्तों के तहत इस्तेमाल करने के लिए सहमत होंगे? बीएसडी या एमआईटी आदर्श होगा, क्योंकि वे नम्पी, पांडो, सिसि, इत्यादि के साथ संगत हैं – naitsirhc

4

एक समाधान (मतदान साथ ) एक पाश

def wait_for_either(a, b): 
    while True: 
     if a.wait(tunable_timeout): 
      break 
     if b.wait(tunable_timeout): 
      break 

मुझे लगता है कि अगर आप धुन टाइमआउट काफी अच्छी तरह से परिणाम ठीक होगा में प्रत्येक Event पर अनुक्रमिक इंतजार करने के लिए किया जाएगा।


सबसे अच्छा गैर मतदान मैं के बारे में सोच सकते हैं एक अलग धागे में हर एक के लिए प्रतीक्षा करें और सेट एक साझा Event जिसे आप मुख्य थ्रेड में के बाद इंतजार करेंगे करने के लिए है।

def repeat_trigger(waiter, trigger): 
    waiter.wait() 
    trigger.set() 

def wait_for_either(a, b): 
    trigger = threading.Event() 
    ta = threading.Thread(target=repeat_trigger, args=(a, trigger)) 
    tb = threading.Thread(target=repeat_trigger, args=(b, trigger)) 
    ta.start() 
    tb.start() 
    # Now do the union waiting 
    trigger.wait() 

सुंदर दिलचस्प है, इसलिए मैं पिछले समाधान का एक OOP संस्करण लिखा है:

class EventUnion(object): 
    """Register Event objects and wait for release when any of them is set""" 
    def __init__(self, ev_list=None): 
     self._trigger = Event() 
     if ev_list: 
      # Make a list of threads, one for each Event 
      self._t_list = [ 
       Thread(target=self._triggerer, args=(ev,)) 
       for ev in ev_list 
      ] 
     else: 
      self._t_list = [] 

    def register(self, ev): 
     """Register a new Event""" 
     self._t_list.append(Thread(target=self._triggerer, args=(ev,))) 

    def wait(self, timeout=None): 
     """Start waiting until any one of the registred Event is set""" 
     # Start all the threads 
     map(lambda t: t.start(), self._t_list) 
     # Now do the union waiting 
     return self._trigger.wait(timeout) 

    def _triggerer(self, ev): 
     ev.wait() 
     self._trigger.set() 
+1

आप repeat_trigger भी ट्रिगर के लिए जांच सकते हैं (टाइमआउट = 0 ट्रिगर और टाइमआउट> 0 वेटर के लिए 0 के साथ) ताकि सभी थ्रेड अंततः –

+0

समाप्त हो सकें, मैं वही सोच रहा था लेकिन 2 धागे शुरू करने से बेहतर तरीका होना चाहिए ... – Claudiu

0

सुंदर नहीं है, लेकिन आप घटनाओं मल्टीप्लेक्स के लिए दो अतिरिक्त धागे का उपयोग कर सकते हैं ...

def wait_for_either(a, b): 
    flag = False #some condition variable, event, or similar 

    class Event_Waiter(threading.Thread): 
    def __init__(self, event): 
     self.e = event 
    def run(self): 
     self.e.wait() 
     flag.set() 

    a_thread = Event_Waiter(a) 
    b_thread = Event_Waiter(b) 
    a.start() 
    b.start() 
    flag.wait() 

नोट, यदि आप बहुत जल्दी पहुंचते हैं तो आपको गलती से दोनों घटनाएं प्राप्त करने की चिंता करनी पड़ सकती है। सहायक धागे (a_thread और b_thread) को ध्वज सेट करने की कोशिश कर चारों ओर सिंक्रनाइज़ करना चाहिए और फिर अन्य धागे को मारना चाहिए (संभवतः उस धागे की घटना को रीसेट करना अगर इसे खपत किया गया हो)।

1

अतिरिक्त धागे शुरू करना एक स्पष्ट समाधान लगता है, हालांकि बहुत प्रभावशाली नहीं है। फ़ंक्शन wait_events किसी भी ईवेंट का उपयोग बंद कर देगा।

def wait_events(*events): 
    event_share = Event() 

    def set_event_share(event): 
     event.wait() 
     event.clear() 
     event_share.set() 
    for event in events: 
     Thread(target=set_event_share(event)).start() 

    event_share.wait() 

wait_events(event1, event2, event3) 
+0

यह जानना अच्छा होगा कि कौन सा ट्रिगर किया गया है – Har

0
def wait_for_event_timeout(*events): 
    while not all([e.isSet() for e in events]): 
     #Check to see if the event is set. Timeout 1 sec. 
     ev_wait_bool=[e.wait(1) for e in events] 
     # Process if all events are set. Change all to any to process if any event set 
     if all(ev_wait_bool): 
      logging.debug('processing event') 
     else: 
      logging.debug('doing other work') 


e1 = threading.Event() 
e2 = threading.Event() 

t3 = threading.Thread(name='non-block-multi', 
         target=wait_for_event_timeout, 
         args=(e1,e2)) 
t3.start() 

logging.debug('Waiting before calling Event.set()') 
time.sleep(5) 
e1.set() 
time.sleep(10) 
e2.set() 
logging.debug('Event is set') 
1

Claudiu's जवाब जहां घटना 1 या घटना 2. या घटना 1 के लिए इंतजार कर सकते हैं या तो और यहां तक ​​कि 2.

from threading import Thread, Event, _Event 

class ConditionalEvent(_Event): 
    def __init__(self, events_list, condition): 
     _Event.__init__(self) 

     self.event_list = events_list 
     self.condition = condition 

     for e in events_list: 
      self._setup(e, self._state_changed) 

     self._state_changed() 

    def _state_changed(self): 
     bools = [e.is_set() for e in self.event_list] 
     if self.condition == 'or': 

      if any(bools): 
       self.set() 
      else: 
       self.clear() 

     elif self.condition == 'and': 

      if all(bools): 
       self.set() 
      else: 
       self.clear() 

    def _custom_set(self,e): 
     e._set() 
     e._state_changed() 

    def _custom_clear(self,e): 
     e._clear() 
     e._state_changed() 

    def _setup(self, e, changed_callback): 
     e._set = e.set 
     e._clear = e.clear 
     e._state_changed = changed_callback 
     e.set = lambda: self._custom_set(e) 
     e.clear = lambda: self._custom_clear(e) 

उदाहरण उपयोग विस्तार से पहले के रूप में बहुत समान हो जाएगा

import time 

e1 = Event() 
e2 = Event() 

or_e = ConditionalEvent([e1, e2], 'or') 


Thread(target=wait_on, args=('e1', e1)).start() 
time.sleep(0.05) 
Thread(target=wait_on, args=('e2', e2)).start() 
time.sleep(0.05) 
Thread(target=wait_on, args=('or_e', or_e)).start() 
time.sleep(0.05) 

print "Firing e1 in 2 seconds..." 
time.sleep(2) 
e1.set() 
time.sleep(0.05) 

print "Firing e2 in 2 seconds..." 
time.sleep(2) 
e2.set() 
time.sleep(0.05) 
संबंधित मुद्दे