2009-01-28 10 views
9

आइए कहें कि मेरे पास टास्किंग नामक एक घटक है (जिसे मैं संशोधित नहीं कर सकता) जो एक विधि "DoTask" का खुलासा करता है जो कुछ संभवतः लंबी गणना करता है और परिणाम को कार्य पूर्ण के माध्यम से परिणाम देता है। आम तौर पर इसे विंडोज़ फॉर्म में कहा जाता है जिसे उपयोगकर्ता परिणाम प्राप्त करने के बाद बंद कर देता है।क्या कॉलर को एक अलग थ्रेड पर इवेंट हैंडलर रखना संभव है?

मेरे विशेष परिदृश्य में मुझे कार्य में लौटाए गए डेटा के साथ कुछ डेटा (डेटाबेस रिकॉर्ड) को जोड़ने की आवश्यकता है और डेटाबेस रिकॉर्ड को अपडेट करने के लिए इसका उपयोग करें।

मैंने ईवेंट को संभालने के दौरान सूचित करने के लिए ऑटोरसेट इवेंट के उपयोग की जांच की है। इसके साथ समस्या AutoResetEvent.WaitOne() ब्लॉक होगी और ईवेंट हैंडलर कभी नहीं बुलाया जाएगा। आम तौर पर AutoResetEvents को एक अलग थ्रेड कहा जाता है, इसलिए मुझे लगता है कि इसका मतलब है कि ईवेंट हैंडलर उसी विधि पर है जो कॉल करने वाली विधि के रूप में होता है।

अनिवार्य रूप से मैं एक एसिंक्रोनस कॉल को चालू करना चाहता हूं, जहां परिणाम किसी ईवेंट के माध्यम से एक सिंक्रोनस कॉल (यानी किसी अन्य वर्ग से DoSyncTask को कॉल करें) में तब तक अवरुद्ध कर दिया जाता है जब तक ईवेंट को संभाला नहीं जाता है और परिणाम किसी स्थान पर पहुंच योग्य होते हैं इवेंट हैंडलर और जिस विधि को एसिंक कॉल शुरू करने वाली विधि कहा जाता है।

public class SyncTask 
{ 
    TaskCompletedEventArgs data; 
    AutoResetEvent taskDone; 

public SyncTask() 
{ 
    taskDone = new AutoResetEvent(false); 
} 

public string DoSyncTask(int latitude, int longitude) 
{ 
    Task t = new Task(); 
    t.Completed = new TaskCompletedEventHandler(TaskCompleted); 
    t.DoTask(latitude, longitude); 
    taskDone.WaitOne(); // but something more like Application.DoEvents(); in WinForms. 
    taskDone.Reset(); 
    return data.Street; 
} 

private void TaskCompleted(object sender, TaskCompletedEventArgs e) 
{ 
    data = e; 
    taskDone.Set(); //or some other mechanism to signal to DoSyncTask that the work is complete. 
} 
} 

In a Windows App the following works correctly. 

public class SyncTask 
{ 
    TaskCompletedEventArgs data; 

public SyncTask() 
{ 
    taskDone = new AutoResetEvent(false); 
} 

public string DoSyncTask(int latitude, int longitude) 
{ 
    Task t = new Task(); 
    t.Completed = new TaskCompletedEventHandler(TaskCompleted); 
    t.DoTask(latitude, longitude); 
    while (data == null) Application.DoEvents(); 

    return data.Street; 
} 

private void TaskCompleted(object sender, TaskCompletedEventArgs e) 
{ 
    data = e; 
} 
} 

मैं सिर्फ एक खिड़की सेवा, जहां Application.Run नहीं बुलाया जाता है और ApplicationContext वस्तु उपलब्ध नहीं है में है कि व्यवहार को दोहराने की जरूरत है।

+0

'ऑटोरसेट इवेंट' को समझने के लिए यहां एक अच्छा वीडियो है http://www.youtube.com/watch?v=xaaRBh07N34 – Jaider

उत्तर

2

शायद आप एक टाइमर ऑब्जेक्ट शुरू करने के लिए DoSyncTask प्राप्त कर सकते हैं जो कुछ उचित अंतराल पर आपके डेटा चर के मान की जांच करता है। एक बार डेटा का मूल्य हो जाने के बाद, आप को यह बताने के लिए एक और घटना आग लग सकती है कि डेटा का मूल्य अब है (और निश्चित रूप से टाइमर बंद करें)।

सुंदर बदसूरत हैक, लेकिन यह सिद्धांत में ... काम कर सकता है।

क्षमा करें, यह सबसे अच्छा है कि मैं आधा सो सकता हूं। बिस्तर के लिए समय ...

+0

लगता है जैसे यह काम कर सकता है। जैसा कि आपने कहा था, बहुत "हैकी" :) –

0

यदि कार्य एक WinForms घटक है, तो यह थ्रेडिंग मुद्दों के बारे में बहुत जागरूक हो सकता है और मुख्य थ्रेड पर ईवेंट हैंडलर को आमंत्रित कर सकता है - जो आप देख रहे हैं।

तो, हो सकता है कि यह एक संदेश पंप या कुछ पर निर्भर हो। एप्लिकेशन.रुन में ओवरलोड हैं जो गैर-जीयूआई ऐप्स के लिए हैं। आप स्टार्टअप और पंप को थ्रेड प्राप्त करने पर विचार कर सकते हैं यह देखने के लिए कि क्या यह समस्या हल करता है।

मैं यह समझने के लिए घटक के स्रोत कोड को देखने के लिए परावर्तक का उपयोग करने की भी सिफारिश करता हूं कि यह क्या कर रहा है।

0

आपको लगभग यह मिल गया है। आपको एक अलग थ्रेड पर चलाने के लिए DoTask विधि की आवश्यकता है, इसलिए WaitOne कॉल काम को रोकने से नहीं रोकेगा। कुछ इस तरह:

Action<int, int> doTaskAction = t.DoTask; 
doTaskAction.BeginInvoke(latitude, longitude, cb => doTaskAction.EndInvoke(cb), null); 
taskDone.WaitOne(); 
+0

आशाजनक लग रहा है। मैं इसे कल आज़मा दूंगा और तदनुसार वोट दूंगा :) –

+0

दुर्भाग्यवश यदि मैं वर्तमान थ्रेड (उपरोक्त के रूप में) पर ईवेंट हैंडलर को हुक करता हूं और एक अलग थ्रेड पर DoTask को कॉल करता हूं, तो ईवेंट हैंडलर अभी भी taskDone.WaitOne() के साथ अवरुद्ध है ; –

+0

क्या यह किसी प्रकार का COM घटक है? कार्यकर्ता धागे पर अपना उदाहरण बनाएं ताकि यह एसटीए थ्रेड पर मार्शल करने की कोशिश न करे। –

2

मैं async के लिए एक समाधान बाहर काम समस्या सिंक करने के लिए, कम से कम सभी .NET वर्गों का उपयोग करना।

http://geekswithblogs.net/rgray/archive/2009/01/29/turning-an-asynchronous-call-into-a-synchronous-call.aspx

यह अभी भी कॉम के साथ काम नहीं करता। मुझे एसटीए थ्रेडिंग की वजह से संदेह है। COM ओसीएक्स को होस्ट करने वाले .NET घटक द्वारा उठाया गया ईवेंट कभी मेरे कार्यकर्ता थ्रेड द्वारा संभाला नहीं जाता है, इसलिए मुझे WaitOne() पर एक डेडलॉक मिलता है।

किसी और हालांकि समाधान की सराहना कर सकते हैं :)

+0

प्रश्न में समान सरलीकरणों का उपयोग करके एसओ पर समाधान को सारांशित करने के लिए विनम्र नहीं होगा? – jwg

0

स्कॉट डब्ल्यू के जवाब पर मेरे टिप्पणी के बाद मैं इसे फिर से पढ़ा है एक छोटे से गुप्त रहा है। तो मुझे और अधिक स्पष्ट हो:

while(!done) 
{ 
    taskDone.WaitOne(200); 
    Application.DoEvents(); 
} 

WaitOne (200) यह आपके यूआई धागा करने के लिए नियंत्रण वापस जाने के लिए प्रति सेकंड 5 बार का कारण (आप इस रूप में आप चाहते हैं समायोजित कर सकते हैं) होगा। DoEvents() कॉल विंडोज इवेंट कतार को फ्लश करेगा (वह जो सभी विंडोज इवेंट हैंडलिंग को पेंटिंग इत्यादि से संभालता है)। अपनी कक्षा में दो सदस्यों को जोड़ें (इस उदाहरण में एक बूल ध्वज "किया गया", और आपके उदाहरण में एक वापसी डेटा "सड़क")।

जो आप करना चाहते हैं उसे पाने का सबसे आसान तरीका है। (मेरे पास अपने ऐप में बहुत ही समान कोड है, इसलिए मुझे पता है कि यह काम करता है)

+0

ओह! साथ ही, वेटऑन का रिटर्न वैल्यू आपको बता सकता है कि यह वापस लौटाया गया है क्योंकि यह समय समाप्त हो गया है या इसे संकेत दिया गया था। आप इसे "पूर्ण" के लिए उपयोग कर सकते हैं। – dviljoen

+0

मैं इसे इस तरह से नहीं करूँगा। मेरी चिंताओं के बारे में अधिक जानकारी http://blogs.msdn.com/jfoscoding/archive/2005/08/06/448560.aspx – ollifant

+0

एप्लिकेशन के साथ समस्या देखें। DEEvents को एप्लिकेशन की आवश्यकता है, जो केवल WinForms ऐप्स के लिए उपलब्ध है। क्लास लाइब्रेरी में कॉलिंग एप्लिकेशन। रुन इत्यादि सिर्फ काम नहीं करता है। –

3

मुझे हाल ही में असीमित कॉल और घटनाओं को धागे पर बनाने और मुख्य धागे पर लौटने के साथ कुछ परेशानी हुई है।

मैंने चीजों का ट्रैक रखने के लिए SynchronizationContext का उपयोग किया। नीचे (छद्म) कोड दिखाता है कि इस समय मेरे लिए क्या काम कर रहा है।

SynchronizationContext context; 

void start() 
{ 
    //First store the current context 
    //to call back to it later 
    context = SynchronizationContext.Current; 

    //Start a thread and make it call 
    //the async method, for example: 
    Proxy.BeginCodeLookup(aVariable, 
        new AsyncCallback(LookupResult), 
        AsyncState); 
    //Now continue with what you were doing 
    //and let the lookup finish 
} 

void LookupResult(IAsyncResult result) 
{ 
    //when the async function is finished 
    //this method is called. It's on 
    //the same thread as the the caller, 
    //BeginCodeLookup in this case. 
    result.AsyncWaitHandle.WaitOne(); 
    var LookupResult= Proxy.EndCodeLookup(result); 
    //The SynchronizationContext.Send method 
    //performs a callback to the thread of the 
    //context, in this case the main thread 
    context.Send(new SendOrPostCallback(OnLookupCompleted), 
       result.AsyncState);       
} 

void OnLookupCompleted(object state) 
{ 
    //now this code will be executed on the 
    //main thread. 
} 

मुझे आशा है कि इससे मदद मिलेगी, क्योंकि यह मेरे लिए समस्या तय करता है।

0

आपका कोड लगभग सही है ... मैं सिर्फ

t.DoTask(latitude, longitude); 

बदल गया है के लिए

new Thread(() => t.DoTask(latitude, longitude)).Start(); 

TaskCompleted एक ही धागे में निष्पादित किया जाएगा के रूप में DoTask करता है। यह काम करना चाहिए।

संबंधित मुद्दे