2012-10-05 14 views
36

में किसी ईवेंट को निकाल दिए जाने तक ब्लॉक करने के लिए कैसे करें this question पूछने के बाद, मुझे आश्चर्य है कि किसी ईवेंट को निकालने के लिए प्रतीक्षा करना संभव है, और उसके बाद ईवेंट डेटा प्राप्त करें और इसका हिस्सा वापस करें। इस तरह का क्रमबद्ध करें:सी #

private event MyEventHandler event; 
public string ReadLine(){ return event.waitForValue().Message; } 
... 
event("My String"); 
...elsewhere... 
var resp = ReadLine(); 

कृपया सुनिश्चित करें कि जो भी समाधान आप प्रदान करते हैं वह इसे किसी अन्य चीज़ से प्राप्त करने के बजाय सीधे मूल्य देता है। मैं पूछ रहा हूं कि ऊपर दी गई विधि किसी तरह से उपलब्ध है या नहीं। मैं ऑटो/मैनुअल रीसेट इवेंट के बारे में जानता हूं, लेकिन मुझे नहीं पता कि वे सीधे वैल्यू वापस लौटाते हैं जैसा मैंने ऊपर किया था।

अद्यतन: मैंने MyEventHandler (जिसमें Message फ़ील्ड शामिल है) का उपयोग करके एक ईवेंट घोषित किया। मेरे पास आग लगने की प्रतीक्षा के लिए ReadLine नामक एक और धागे में एक विधि है। जब ईवेंट WaitForValue विधि (ईवेंट हैंडलिंग दृश्य का हिस्सा) निकालता है तो ईवेंट तर्क देता है, जिसमें संदेश होता है। संदेश तब रीडलाइन द्वारा लौटाया जाता है जिसे जिसे भी कहा जाता है।

The accepted answer से that question मैंने पूछा कि मैंने क्या किया था, लेकिन यह बिल्कुल सही नहीं लगता है। यह लगभग लगता है जैसे मैनुअल रीसेट इवेंट फायरिंग और डेटा को पुनर्प्राप्त करने और इसे वापस करने के बीच डेटा के साथ कुछ हो सकता है।

अद्यतन:Auto/ManualResetEvent के साथ मुख्य समस्या यह है कि यह बहुत कमजोर है। एक धागा घटना के लिए इंतजार कर सकता है, और फिर किसी और के लिए इसे बदलने से पहले इसे पाने के लिए पर्याप्त समय नहीं देता है। क्या ताले या कुछ और उपयोग करने का कोई तरीका है? हो सकता है कि प्राप्त करें और सेट स्टेटमेंट का उपयोग करें।

+0

क्या करने के लिए बदल

await tsc.Task; 

थोड़ी देर के पाश के बारे में: 'जबकि (someGlobalvar);' 'cahnge एक समारोह आप इस घटना के लिए asign में someGlobalvar'। – elyashiv

+9

यह सक्रिय प्रतीक्षा है, और यह एक बहुत ही खराब समाधान है –

+0

अब, 2 साल बाद, ऐसा लगता है कि मुझे केवल वेटटवो बनाना चाहिए जो किसी ऑब्जेक्ट को वापस करेगा, और '* ResetEvent.Set (ऑब्जेक्ट डेटा)'। मुझे अभी भी ऑब्जेक्ट संदर्भों के बारे में चिंता करने की आवश्यकता होगी, लेकिन यह सामान्य है। कम से कम सूचक नहीं बदलेगा। –

उत्तर

25

आप मैन्युअल रीसेट इवेंट का उपयोग कर सकते हैं। द्वितीयक थ्रेड को आग लगाने से पहले ईवेंट को रीसेट करें और फिर मौजूदा थ्रेड को अवरुद्ध करने के लिए WaitOne() विधि का उपयोग करें। इसके बाद आप द्वितीयक थ्रेड को मैन्युअल रीसेट इवेंट सेट कर सकते हैं जो मुख्य धागे को जारी रखेगा। कुछ इस तरह: घटना आप के लिए इंतजार कर सकते हैं की

ManualResetEvent oSignalEvent = new ManualResetEvent(false); 

void SecondThread(){ 
    //DoStuff 
    oSignalEvent.Set(); 
} 

void Main(){ 
    //DoStuff 
    //Call second thread 
    System.Threading.Thread oSecondThread = new System.Threading.Thread(SecondThread); 
    oSecondThread.Start(); 

    oSignalEvent.WaitOne(); //This thread will block here until the reset event is sent. 
    oSignalEvent.Reset(); 
    //Do more stuff 
} 
+0

यह मूल्य सीधे वापस नहीं करता है। मैंने पहले ही दूसरे प्रश्न में कोशिश की है। क्या ऐसा कुछ नहीं है जिसके बारे में मैं पूछ रहा हूं? –

2

एक बहुत ही आसान तरह ManualResetEvent, ManualResetEventSlim है, और भी बेहतर।

उनके पास WaitOne() विधि है जो ठीक है। आप हमेशा के लिए प्रतीक्षा कर सकते हैं, या एक टाइमआउट सेट कर सकते हैं, या "रद्दीकरण टोकन" जो आपके लिए ईवेंट का इंतजार करना बंद करने का निर्णय लेता है (यदि आप अपना काम रद्द करना चाहते हैं, या आपके ऐप से बाहर निकलने के लिए कहा जाता है)।

आप उन्हें Set() पर कॉल करने के लिए आग लगाते हैं।

Here दस्तावेज़ है।

+1

लेकिन यह किसी भी प्रकार का मूल्य नहीं लौटाता है। –

+0

क्या आप qu संपादित कर सकते हैं और विभिन्न घटकों और उनके इंटरैक्शन का एक अवलोकन जोड़ सकते हैं? यह हमें बेहतर देखने में मदद करेगा कि आप क्या हासिल करने की कोशिश कर रहे हैं और उम्मीद है कि एक बेहतर डिजाइन का नेतृत्व करें। – Michael

-1

आप Microsoft रिएक्टिव एक्सटेंशन का उपयोग करने में खुशी होती है, तो यह अच्छी तरह से काम कर सकते हैं:

var foo = new Foo(); 

ThreadPoolScheduler.Instance 
    .Schedule(
     TimeSpan.FromSeconds(5.0), 
     () => foo.SendLine("Bar!")); 

var resp = foo.ReadLine(); 

Console.WriteLine(resp); 

मैं SendLine संदेश कॉल करने के लिए की जरूरत:

public class Foo 
{ 
    public delegate void MyEventHandler(object source, MessageEventArgs args); 
    public event MyEventHandler _event; 
    public string ReadLine() 
    { 
     return Observable 
      .FromEventPattern<MyEventHandler, MessageEventArgs>(
       h => this._event += h, 
       h => this._event -= h) 
      .Select(ep => ep.EventArgs.Message) 
      .First(); 
    } 
    public void SendLine(string message) 
    { 
     _event(this, new MessageEventArgs() { Message = message }); 
    } 
} 

public class MessageEventArgs : EventArgs 
{ 
    public string Message; 
} 

मैं इसे इस तरह उपयोग कर सकते हैं लॉकिंग से बचने के लिए एक अलग धागे पर, लेकिन यह कोड दिखाता है कि यह अपेक्षा के अनुसार काम करता है।

13

यदि वर्तमान विधि async है तो आप कार्य पूर्णीकरण स्रोत का उपयोग कर सकते हैं। एक ऐसा फ़ील्ड बनाएं जो ईवेंट हैंडलर और वर्तमान विधि तक पहुंच सके।

TaskCompletionSource<bool> tsc = null; 

    private async void Button_Click(object sender, RoutedEventArgs e) 
    { 
     tsc = new TaskCompletionSource<bool>(); 
     await tsc.Task; 
     WelcomeTitle.Text = "Finished work"; 
    } 

    private void Button_Click2(object sender, RoutedEventArgs e) 
    { 
     tsc?.TrySetResult(true); 
    } 

यह उदाहरण एक ऐसे फॉर्म का उपयोग करता है जिसमें वेलकमटाइट और दो बटन नामक टेक्स्टब्लॉक होता है। जब पहला बटन क्लिक किया जाता है तो यह क्लिक ईवेंट शुरू करता है लेकिन प्रतीक्षा लाइन पर रुक जाता है। जब दूसरा बटन क्लिक किया जाता है तो कार्य पूरा हो जाता है और वेलकमटाइट टेक्स्ट अपडेट किया जाता है। तुम भी समय-समाप्त करना चाहते हैं तो

await Task.WhenAny(tsc.Task, Task.Delay(25000)); 
if (tsc.Task.IsCompleted) 
    WelcomeTitle.Text = "Task Completed"; 
else 
    WelcomeTitle.Text = "Task Timed Out"; 
+0

आपको कार्यप्रणाली स्रोत –

+0

का उपयोग करने के लिए async की आवश्यकता नहीं है, यह सच है, लेकिन यदि आप UI को अवरुद्ध किए बिना कार्य का इंतजार करना चाहते हैं तो आपको इसकी आवश्यकता है। – Adam