C # async callback still on background thread ... help! (preferably without InvokeRequired)

I am writing a very simple asynchronous helper class to go along with my project. The purpose of the class is that it allows you to use the method in the background thread. Here is the code:

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 } 

The result of the task is then returned through the OnOperationComplete event. The problem is that when the event rises, it is still in the background thread. That is, if I try to run this code (below), I get a cross-thread error;

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

Please communicate any thoughts.

+7
generics c # asynchronous events delegates
source share
5 answers

Use the BackgroundWorker class. He essentially does the same thing as you.

  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"; } 
+5
source share

I recommend using the Task class rather than BackgroundWorker , but will either significantly outperform Control.Invoke or Dispatcher.Invoke .

Example:

 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); } 

This is very similar to BackgroundWorker , though (other than adding a timer). You may consider using BackgroundWorker .

+3
source share

You need to trigger your event in the user interface thread,

Winforms

Form1.BeginInvoke(...);

WPF

Dispatcher.BeginInvoke(...);

0
source share

Use the BackgroundWorker class, you basically override it here.

0
source share

If you do not create your help class for internal context switching, you will always need to call it in the event handler, because in your code above you raise the event in a non-ui stream.

To do this, your helper must know how to return to the ui thread. You can pass ISynchronizeInvoke to the helper and then use it when it is done. Something like:

 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}); } 

The Control class implements ISynchronizeInvoke , so you can pass the this pointer from Form or Control , which calls the helper and has an event handler delegate,

0
source share

All Articles