2010-09-04 7 views
7

मैं अपनी परियोजना के साथ जाने के लिए एक बहुत ही सरल असीमित सहायक वर्ग लिख रहा हूं। कक्षा का उद्देश्य यह है कि यह पृष्ठभूमि धागे पर एक विधि को चलाने की अनुमति देता है। कोड यहाँ है;सी # async कॉलबैक अभी भी पृष्ठभूमि धागे पर ... मदद! (अधिमानतः बिना InvokeRequired)


    internal class AsyncHelper 
    { 
     private readonly Stopwatch timer = new Stopwatch(); 
     internal event DownloadCompleteHandler OnOperationComplete; 

     internal void Start(Func func, T arg) 
     { 
      timer.Start(); 
      func.BeginInvoke(Done, func); 
     } 

     private void Done(IAsyncResult cookie) 
     { 
      timer.Stop(); 
      var target = (Func) cookie.AsyncState; 
      InvokeCompleteEventArgs(target.EndInvoke(cookie)); 
     } 

     private void InvokeCompleteEventArgs(T result) 
     { 
      var args = new EventArgs(result, null, AsyncMethod.GetEventByClass, timer.Elapsed); 
      if (OnOperationComplete != null) OnOperationComplete(null, args); 
     } 

     #region Nested type: DownloadCompleteHandler 

     internal delegate void DownloadCompleteHandler(object sender, EventArgs e); 

     #endregion 
    } 

कार्य का परिणाम तो OnOperationComplete घटना के माध्यम से दिया जाता है। समस्या यह है कि जब घटना उठाई जाती है, तो यह अभी भी पृष्ठभूमि धागे पर है। अर्थात। अगर मैं इस कोड को चलाने की कोशिश करता हूं (नीचे) मुझे क्रॉस थ्रेडिंग त्रुटि मिलती है;

txtOutput.AppendText(e.Result + Environment.NewLine);

कृपया किसी भी विचार की सलाह दें।

+0

क्या आप .NET 2.0, 3.5, 4.0 पर हैं? और क्या आप WinForms, WPF, सिल्वरलाइट एप्लिकेशन बना रहे हैं? –

+0

.NET 4.0, कक्षा पुस्तकालय –

उत्तर

5

पृष्ठभूमिवर्कर वर्ग का उपयोग करें। यह अनिवार्य रूप से वही करता है जो आप चाहते हैं।

 private BackgroundWorker _worker; 

    public Form1() 
    { 
     InitializeComponent(); 
     _worker = new BackgroundWorker(); 
     _worker.DoWork += Worker_DoWork; 
     _worker.RunWorkerCompleted += Work_Completed; 
    } 

    private void Work_Completed(object sender, RunWorkerCompletedEventArgs e) 
    { 
     txtOutput.Text = e.Result.ToString(); 
    } 

    private void Worker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     e.Result = "Text received from long runing operation"; 
    } 
+0

आप प्रगति सेटिंग की रिपोर्ट भी कर सकते हैं: _worker.WorkerReportsProgress = true; // और _worker.ProgressChanged + = Progress_Changed; –

+0

मैं थोड़े भूल गया ... वास्तव में कार्यकर्ता को चलाने के लिए आपको _worker.RunWorkerAsync() विधि को कॉल करना होगा। –

+0

आपके उत्तर के तहत थोड़ा "संपादन" लिंक है। :) – Timwi

0

आप यूआई धागे पर अपने घटना को लागू करने की जरूरत है,

WinForms

Form1.BeginInvoke(...);

WPF

Dispatcher.BeginInvoke(...);

+0

क्या आप इसका सुझाव दे रहे हैं ?; OnOperationComplete.BeginInvoke

+0

'ISynchronizeInvoke' पुराना इंटरफ़ेस है और इसका उपयोग नहीं किया जाना चाहिए। –

+1

@ जोन, नहीं, वह सुझाव दे रहा है कि आप विजुअल नियंत्रण पर पाए गए BeginInvoke या Invoke विधि को कॉल करें। इस तरह आप जीयूआई थ्रेड पर कॉल को मार्शल करेंगे, और इससे आपको जीयूआई राज्य में हेरफेर करने की अनुमति मिल जाएगी। 'प्रतिनिधि। बेगिन इनवोक' और 'कंट्रोल। बेगीन इनवोक' एक ही बात नहीं है। –

0

उपयोग BackgroundWorker वर्ग, आप मूल रूप से यह reimplementing रहे हैं यहाँ।

0

जब तक आप संदर्भ करने के लिए आपकी मदद की कक्षा का निर्माण आंतरिक रूप से स्विच आप हमेशा ऊपर आप गैर ui धागे पर घटना की परवरिश कर रहे हैं क्योंकि अपने कोड में ईवेंट हैंडलर में आह्वान करने के लिए की आवश्यकता होगी।

ऐसा करने के लिए आपके सहायक को यह जानने की जरूरत है कि यूई थ्रेड पर वापस कैसे जाना है। आप सहायक के लिए ISynchronizeInvoke पास कर सकते हैं और फिर पूरा होने पर इसका उपयोग कर सकते हैं।

ISynchronizeInvoke _sync; 
internal void Start(Func func, T arg, ISynchronizeInvoke sync) 
{ 
    timer.Start(); 
    func.BeginInvoke(Done, func); 
    _sync = sync; 
} 

private void InvokeCompleteEventArgs(T result) 
{ 
    var args = new EventArgs(result, null, AsyncMethod.GetEventByClass, timer.Elapsed); 
    if (OnOperationComplete != null) 
    _sync.Invoke(OnOperationComplete, new object[]{null, args}); 
} 

Control वर्ग ISynchronizeInvoke लागू करता है ताकि आप Form या Control कि सहायक कॉल और ईवेंट हैंडलर प्रतिनिधि है से this सूचक पारित कर सकते हैं,

+0

'ISynchronizeInvoke' पुराना इंटरफ़ेस है और इसका उपयोग नहीं किया जाना चाहिए। –

+0

@Stephen - संदर्भ? – dkackman

3

मैं बजाय Task वर्ग का उपयोग करना चाहिये: की तरह Somwthing BackgroundWorker, लेकिन either would be greatly superior to Control.Invoke or Dispatcher.Invoke.

उदाहरण:

internal class AsyncHelper<T> 
{ 
    private readonly Stopwatch timer = new Stopwatch(); 
    private readonly TaskScheduler ui; 

    // This should be called from a UI thread. 
    internal AsyncHelper() 
    { 
    this.ui = TaskScheduler.FromCurrentSynchronizationContext(); 
    } 

    internal event DownloadCompleteHandler OnOperationComplete; 

    internal Task Start(Func<T> func) 
    { 
    timer.Start(); 
    Task.Factory.StartNew(func).ContinueWith(this.Done, this.ui); 
    } 

    private void Done(Task<T> task) 
    { 
    timer.Stop(); 
    if (task.Exception != null) 
    { 
     // handle error condition 
    } 
    else 
    { 
     InvokeCompleteEventArgs(task.Result); 
    } 
    } 

    private void InvokeCompleteEventArgs(T result) 
    { 
    var args = new EventArgs(result, null, AsyncMethod.GetEventByClass, timer.Elapsed); 
    if (OnOperationComplete != null) OnOperationComplete(null, args); 
    } 

    internal delegate void DownloadCompleteHandler(object sender, EventArgs e); 
} 

यह BackgroundWorker के समान है, हालांकि (सिवाय इसके कि आप एक टाइमर जोड़ रहे हैं)। आप केवल BackgroundWorker का उपयोग करने पर विचार करना चाहेंगे।

+0

क्या आपने इसे पढ़ा? मेरे "तीन पृष्ठों" में यूजर इंटरफेस के साथ एक पूर्ण समाधान, रद्दीकरण के लिए समर्थन, उचित त्रुटि मार्शलिंग आदि शामिल हैं। इनमें से कोई भी आपके समाधान में शामिल नहीं है। –

+0

मैंने अपने उत्तर को एक सरल समाधान के साथ अपडेट किया है। –

+0

मुझे यह पसंद है लेकिन यह सुनिश्चित नहीं है कि कैसे कार्यान्वित किया जाए, क्या आप उपयोग उदाहरण दे सकते हैं? –

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