2009-01-29 16 views
35

कभी-कभी घटना होने की प्रतीक्षा करते समय यह मेरे धागे को अवरुद्ध करना चाहता है।किसी ईवेंट के लिए अवरुद्ध और प्रतीक्षा

मैं आमतौर पर यह कुछ इस तरह करते हैं:

private AutoResetEvent _autoResetEvent = new AutoResetEvent(false); 

private void OnEvent(object sender, EventArgs e){ 
    _autoResetEvent.Set(); 
} 

// ... 
button.Click += OnEvent; 
try{ 
    _autoResetEvent.WaitOne(); 
} 
finally{ 
    button.Click -= OnEvent; 
} 

हालांकि, ऐसा लगता है कि यह कुछ है कि मैं (या शायद यह भी कुछ है जो पहले से ही ढांचे में मौजूद है) एक आम वर्ग के लिए निकाल सकते होना चाहिए।

मैं इस तरह कुछ करने के लिए सक्षम होने के लिए करना चाहते हैं:

EventWaiter ew = new EventWaiter(button.Click); 
ew.WaitOne(); 
EventWaiter ew2 = new EventWaiter(form.Closing); 
ew2.WaitOne(); 

लेकिन मैं वास्तव में (जैसे कि एक वर्ग के निर्माण के लिए एक तरह से मैं पारित करने के लिए एक अच्छा वैध रास्ता नहीं मिल सकता है नहीं मिल सकता है एक तर्क के रूप में घटना)। क्या कोई मदद कर सकता है?

var status = ShowStatusForm(); 
status.ShowInsertUsbStick(); 
bool cancelled = WaitForUsbStickOrCancel(); 
if(!cancelled){ 
    status.ShowWritingOnUsbStick(); 
    WriteOnUsbStick(); 
    status.AskUserToRemoveUsbStick(); 
    WaitForUsbStickToBeRemoved(); 
    status.ShowFinished(); 
}else{ 
    status.ShowCancelled(); 
} 
status.WaitUntilUserPressesDone(); 

यह बहुत अधिक संक्षिप्त और तर्क कई तरीकों के बीच फैला के साथ लिखा बराबर कोड से पढ़ी जा सकती है:

क्यों यह उपयोगी हो सकता है का एक उदाहरण देने के लिए, कुछ इस तरह विचार करें। लेकिन WaitForUsbStickOrCancel() को लागू करने के लिए, WaitForUsbStickToBeRemoved और WaitUntilUserPressesDone() (मान लीजिए कि जब हमें यूएसबी स्टिक डाली जाती है या निकाल दिया जाता है तो हमें एक ईवेंट मिलता है) मुझे हर बार "EventWaiter" को फिर से लागू करने की आवश्यकता होती है। बेशक आपको जीयूआई-थ्रेड पर इसे चलाने के लिए सावधान रहना होगा, लेकिन कभी-कभी यह सरल कोड के लिए एक सार्थक ट्रेडऑफ है।

विकल्प कुछ इस तरह दिखेगा:

var status = ShowStatusForm(); 
status.ShowInsertUsbStick(); 
usbHandler.Inserted += OnInserted; 
status.Cancel += OnCancel; 
//... 
void OnInserted(/*..*/){ 
    usbHandler.Inserted -= OnInserted; 
    status.ShowWritingOnUsbStick(); 
    MethodInvoker mi =() => WriteOnUsbStick(); 
    mi.BeginInvoke(WritingDone, null); 
} 
void WritingDone(/*..*/){ 
    /* EndInvoke */ 
    status.AskUserToRemoveUsbStick(); 
    usbHandler.Removed += OnRemoved; 
} 
void OnRemoved(/*..*/){ 
    usbHandler.Removed -= OnRemoved; 
    status.ShowFinished(); 
    status.Done += OnDone; 
} 
/* etc */ 

मुझे लगता है कि बहुत कठिन पढ़ने के लिए लगता है। माना जाता है कि यह हमेशा से दूर है कि प्रवाह इतना रैखिक होगा, लेकिन जब यह है, मुझे पहली शैली पसंद है।

यह ShowMessage() और Form.ShowDialog() का उपयोग करने के लिए तुलनीय है - वे कुछ "ईवेंट" होने तक भी अवरुद्ध होते हैं (हालांकि वे एक संदेश-लूप चलाएंगे यदि उन्हें गुई-थ्रेड पर बुलाया जाता है)।

+0

क्यों वास्तव में आप ऐसा करना चाहते हैं चाहते हैं मैं उत्सुक हूँ ... आप विस्तार से बता सकते हैं? –

+0

मुझे अभी भी समझ में नहीं आ रहा है कि आप घटना के इंतजार के बजाय धागे को अवरुद्ध क्यों करना चाहते हैं - यह क्या करता है? –

+0

क्या आपने कभी इसे ठीक किया है? मुझे भी यही तकलीफ़ है। – Carlo

उत्तर

3

घटना को पास न करें, ईवेंट प्रतिनिधि हैं जो ईवेंट हैंडलर हस्ताक्षर से मेल खाता है। यह वास्तव में मुझे हैकी लगता है, इसलिए संभावित मृत लॉक मुद्दों से अवगत रहें।

+0

मुझे यकीन नहीं है कि मैं समझता हूं। क्या आपका मतलब उन प्रतिनिधियों को गुजरना है जो ईवेंट से सदस्यता लेते हैं और सदस्यता छोड़ते हैं (यानी नया इवेंटवाइटर (eh => {button.Click + = eh;}, eh => {button.Click - = eh;})) यह काम करेगा, मुझे लगता है, लेकिन मैं कुछ आसान पसंद करूंगा। –

-7

मुझे लगता है कि इन्हें काम करना चाहिए, सिर्फ कोडित करने की कोशिश नहीं की गई।

public class EventWaiter<T> where T : EventArgs 
{ 
    private System.Threading.ManualResetEvent manualEvent; 

    public EventWaiter(T e) 
    { 
     manualEvent = new System.Threading.ManualResetEvent(false); 
     e += this.OnEvent; 
    } 

    public void OnEvent(object sender, EventArgs e) 
    { 
     manualEvent.Set(); 
    } 

    public void WaitOne() 
    { 
     manualEvent.WaitOne(); 
    } 

    public void Reset() 
    { 
     manualEvent.Reset(); 
    } 
} 

बहुत ज्यादा नहीं सोचा था, लेकिन यह पता नहीं लगा सकता कि इसे EventArgs से अलग कैसे किया जाए।

MSDN ManualResetEvent पर एक नज़र डालें और आपको पता चलेगा कि आप श्रृंखला की तरह इंतजार कर सकते हैं और कुछ अजीब चीजें हैं।

+1

यह इवेंटअर्ग्स के साथ वास्तव में कोई समस्या नहीं है। इसे EventArgs-type के बारे में सामान्य बनाने में कोई समस्या नहीं होगी। समस्या यह है कि समस्या यह है कि समस्या किसी ईवेंट को किसी ईवेंट में पास करने का कोई तरीका नहीं प्रतीत होता है, ताकि आप एक ईवेंट-हैंडलर संलग्न कर सकें (यह आपके ईवेंट में जैसा इवेंट एआरजी संलग्न नहीं है)। –

+0

ओएमजी !! हाँ आप सही हैं मैं नहीं मुझे नहीं पता था कि मैं क्या सोच रहा था!: एस – brutuscat

0

मैंने प्रतिबिंब का उपयोग करके लिंककैड में एक कामकाजी नमूना एक साथ पहुंचाया है, एक स्ट्रिंग के साथ EventInfo ऑब्जेक्ट का संदर्भ प्राप्त कर रहा है (सावधान रहें क्योंकि आप संकलन समय जांच को ढीला करते हैं)। स्पष्ट मुद्दा यह है कि कोई गारेंटी नहीं है, एक कार्यक्रम कभी भी निकाल दिया जाएगा, या इवेंटवाइटर क्लास ब्लॉकिंग शुरू करने के लिए तैयार होने से पहले आपकी उम्मीद को निकाल दिया जा सकता है, इसलिए मुझे यकीन नहीं है कि अगर मैं इसे डालूं तो मैं आराम से सो जाऊंगा एक उत्पादन ऐप।

void Main() 
{ 
    Console.WriteLine("main thread started"); 

    var workerClass = new WorkerClassWithEvent(); 
    workerClass.PerformWork(); 

    var waiter = new EventWaiter(workerClass, "WorkCompletedEvent"); 
    waiter.WaitForEvent(TimeSpan.FromSeconds(10)); 

    Console.WriteLine("main thread continues after waiting"); 
} 

public class WorkerClassWithEvent 
{ 
    public void PerformWork() 
    { 
     var worker = new BackgroundWorker(); 
     worker.DoWork += (s, e) => 
     { 
      Console.WriteLine("threaded work started"); 
      Thread.Sleep(1000); // <= the work 
      Console.WriteLine("threaded work complete"); 
     }; 
     worker.RunWorkerCompleted += (s, e) => 
     { 
      FireWorkCompletedEvent(); 
      Console.WriteLine("work complete event fired"); 
     }; 

     worker.RunWorkerAsync(); 
    } 

    public event Action WorkCompletedEvent; 
    private void FireWorkCompletedEvent() 
    { 
     if (WorkCompletedEvent != null) WorkCompletedEvent(); 
    } 
} 

public class EventWaiter 
{ 
    private AutoResetEvent _autoResetEvent = new AutoResetEvent(false); 
    private EventInfo _event    = null; 
    private object _eventContainer   = null; 

    public EventWaiter(object eventContainer, string eventName) 
    { 
     _eventContainer = eventContainer; 
     _event = eventContainer.GetType().GetEvent(eventName); 
    } 

    public void WaitForEvent(TimeSpan timeout) 
    { 
     _event.AddEventHandler(_eventContainer, (Action)delegate { _autoResetEvent.Set(); }); 
     _autoResetEvent.WaitOne(timeout); 
    } 
} 

आउटपुट

// main thread started 
// threaded work started 
// threaded work complete 
// work complete event fired 
// main thread continues after waiting 
3

मैं मृत संशोधित।EventHandler<T> को संभालने के लिए रबीट की कक्षा इवेंटवाइटर। तो आप EventHandler<T> के सभी ईवेंट प्रकारों का इंतजार करने के लिए उपयोग कर सकते हैं, इसका मतलब है कि आपका प्रतिनिधि delegate void SomeDelegate(object sender, T EventsArgs) जैसा कुछ है।

public class EventWaiter<T> 
{ 

    private AutoResetEvent _autoResetEvent = new AutoResetEvent(false); 
    private EventInfo _event = null; 
    private object _eventContainer = null; 

    public EventWaiter(object eventContainer, string eventName) 
    { 
     _eventContainer = eventContainer; 
     _event = eventContainer.GetType().GetEvent(eventName); 
    } 

    public void WaitForEvent(TimeSpan timeout) 
    { 
     EventHandler<T> eventHandler = new EventHandler<T>((sender, args) => { _autoResetEvent.Set(); }); 
     _event.AddEventHandler(_eventContainer, eventHandler); 
     _autoResetEvent.WaitOne(timeout); 
     _event.RemoveEventHandler(_eventContainer, eventHandler); 
    } 
} 

और उदाहरण के लिए मैं का उपयोग करें कि जब मैं खिड़कियों के लिए पंजीकरण HttpNotificationChannel से यूआरएल पाने के लिए इंतजार कर के लिए धक्का अधिसूचना सेवा।

  HttpNotificationChannel pushChannel = new HttpNotificationChannel(channelName); 
      //ChannelUriUpdated is event 
      EventWaiter<NotificationChannelUriEventArgs> ew = new EventWaiter<NotificationChannelUriEventArgs>(pushChannel, "ChannelUriUpdated"); 
      pushChannel.Open(); 
      ew.WaitForEvent(TimeSpan.FromSeconds(30)); 
0

आप भी इस कोशिश कर सकते हैं:

class EventWaiter<TEventArgs> where TEventArgs : EventArgs 
{ 
    private readonly Action<EventHandler<TEventArgs>> _unsubHandler; 
    private readonly Action<EventHandler<TEventArgs>> _subHandler; 

    public EventWaiter(Action<EventHandler<TEventArgs>> subHandler, Action<EventHandler<TEventArgs>> unsubHandler) 
    { 
     _unsubHandler = unsubHandler; 
     _subHandler = subHandler; 
    } 

    protected void Handler(object sender, TEventArgs args) 
    { 
     _unsubHandler.Invoke(Handler); 
     TaskCompletionSource.SetResult(args); 
    } 

    public TEventArgs WaitOnce() 
    { 
     TaskCompletionSource = new TaskCompletionSource<TEventArgs>(); 
     _subHandler.Invoke(Handler); 
     return TaskCompletionSource.Task.Result; 
    } 

    protected TaskCompletionSource<TEventArgs> TaskCompletionSource { get; set; } 

} 

उपयोग:

EventArgs eventArgs = new EventWaiter<EventArgs>((h) => { button.Click += new EventHandler(h); }, (h) => { button.Click -= new EventHandler(h); }).WaitOnce(); 
संबंधित मुद्दे