Problem:
If you run the RunWorkerAsync method of normal BackgroundWorker class when a previous RunWorkerAsync has not yet been completed, then you will get an exception. What this means is that before calling the RunWorkerAsync again, you must be sure that the Completed event of the previous call has been reached.
Solution:
Features of my QueuedBackgroundWorker:
- Run different methods asynchronously and get the result back if you would want to.
- All the works will be queued and will run one by one.
- You can have complex input or output arguments.
- It’s thread-safe
QueuedBackgroundWorker:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;
/// <summary>
/// This is thread-safe
/// </summary>
public class QueuedBackgroundWorker
{
#region Constructors
public QueuedBackgroundWorker() { }
#endregion
#region Properties
Queue<object> Queue = new Queue<object>();
object lockingObject1 = new object();
public delegate void WorkerCompletedDelegate<K>(K result, Exception error);
#endregion
#region Methods
/// <summary>
/// doWork is a method with one argument
/// </summary>
/// <typeparam name="T">is the type of the input parameter</typeparam>
/// <typeparam name="K">is the type of the output result</typeparam>
/// <param name="inputArgument"></param>
/// <param name="doWork"></param>
/// <param name="workerCompleted"></param>
public void RunAsync<T,K>(Func<T, K> doWork, T inputArgument, WorkerCompletedDelegate<K> workerCompleted)
{
BackgroundWorker bw = GetBackgroundWorker<T,K>(doWork, workerCompleted);
Queue.Enqueue(new QueueItem(bw, inputArgument));
lock (lockingObject1)
{
if (Queue.Count == 1)
{
((QueueItem)this.Queue.Peek()).RunWorkerAsync();
}
}
}
/// <summary>
/// Use this method if you don't need to handle when the worker is completed
/// </summary>
/// <param name="doWork"></param>
/// <param name="inputArgument"></param>
public void RunAsync<T,K>(Func<T, K> doWork, T inputArgument)
{
RunAsync(doWork, inputArgument, null);
}
private BackgroundWorker GetBackgroundWorker<T, K>(Func<T, K> doWork, WorkerCompletedDelegate<K> workerCompleted)
{
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = false;
bw.WorkerSupportsCancellation = false;
bw.DoWork += (sender, args) =>
{
if (doWork != null)
{
args.Result = (K)doWork((T)args.Argument);
}
};
bw.RunWorkerCompleted += (sender, args) =>
{
if (workerCompleted != null)
{
workerCompleted((K)args.Result, args.Error);
}
Queue.Dequeue();
lock (lockingObject1)
{
if (Queue.Count > 0)
{
((QueueItem)this.Queue.Peek()).RunWorkerAsync();
}
}
};
return bw;
}
#endregion
}
public class QueueItem
{
#region Constructors
public QueueItem(BackgroundWorker backgroundWorker, object argument)
{
this.BackgroundWorker = backgroundWorker;
this.Argument = argument;
}
#endregion
#region Properties
public object Argument { get; private set; }
public BackgroundWorker BackgroundWorker { get; private set; }
#endregion
#region Methods
public void RunWorkerAsync()
{
this.BackgroundWorker.RunWorkerAsync(this.Argument);
}
#endregion
}
1) How to use it:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
var worker = new QueuedBackgroundWorker();
worker.RunAsync<int, int>(Calculate, 2);
worker.RunAsync<MultiplyArgument, int>(Multiply, new MultiplyArgument() { A = 1, B = 2 }, MultiplyCompleted);
}
private int Calculate(int a)
{
return a*2;
}
private int Multiply(MultiplyArgument calculateArgument)
{
return calculateArgument.A * calculateArgument.B;
}
private void MultiplyCompleted(int result, Exception error)
{
Response.Write("worker completed, result: " + result.ToString());
}
}
public class MultiplyArgument
{
public int A { get; set; }
public int B { get; set; }
}
7 comments:
it is working very good and solved my problem thanks a lot man
I just want to say thanks for this, it helped me a lot, really. People like you deserves heaven, hehehe. really appreciate it, thanks again
How would you stop it .. say you've got all your queued work going then you need to stop for any reason .. not to resume .. but pausing would also be good .
What if I have a function with 3 arguements that needs to be queued using this class? How do I write the expression?
Say the function is called normally as playAudiofile(thisCounter.counter_prefix, thisCounter.counter_name, thisCounter.token_number);
And I am using backgroundworker1 control. Should i remove that?
Okay i got that working. i would like to know that if there are many timers on the application, then would the thread sync be affected? Because Im trying to play a series of wav files via wmplib and it happens on button click.
so if i click the button 3 times, the 2nd thread and 3rd thread overlap with the 1st one. Why is that happening?
Also, implement IDisposable, as the BackgroundWorker implements the IDisposable interface.
Post a Comment