How to cancel this Queued BackGroundworker - c#

I have this bit of legacy code that's been sitting in the code-base and being used for the last couple of years, recently we found that its method StopImmediately() doesn't stop it at all. I cant make head nor tail of how it works as I've done very little with threads or background workers.
I was wondering if any of the experienced threading fellas could tell me how to stop this confusing little beast from completing its task. Below is the complete class (sorry for the amount of code)
I cannot figure out how to cancel it .. thanks in advance for any help ..
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
namespace GPS2.BackgroundWorkerEx
{
public class QedBackgroundWorker
{
public QedBackgroundWorker() {}
Queue<object> Queue = new Queue<object>();
object lockingObject1 = new object();
private Thread currentThread;
public delegate void WorkerCompletedDelegate<K>(K result, Exception error);
public object Arguments { get; set; }
/// <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();
//currentThread = System.Threading.Thread.CurrentThread;
}
}
}
/// <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 = true;
bw.DoWork += (sender, args) =>{
if (doWork != null)
{
args.Result = (K)doWork((T)args.Argument);
currentThread = System.Threading.Thread.CurrentThread;
}
};
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;
}
public void StopImmediately()
{
if (currentThread != null)
currentThread.Abort();
}
public bool IsBusy()
{
ThreadState state = currentThread.ThreadState;
bool res = true;
switch (state)
{
case ThreadState.Running:
res = true;
break;
default:
res = false;
break;
}
return res;
}
}
public class QueueItem{
public QueueItem(BackgroundWorker backgroundWorker, object argument)
{
this.BackgroundWorker = backgroundWorker;
this.Argument = argument;
}
public object Argument { get; private set; }
public BackgroundWorker BackgroundWorker { get; private set; }
public void RunWorkerAsync()
{
this.BackgroundWorker.RunWorkerAsync(this.Argument);
}
}
}

The typical way for a BackgroundWorker to support cancellation is for the DoWork event handler to periodically check for the BackgroundWorker's CancellationPending property:
var worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += (sender, args) =>
{
foreach (var thing in listOfThingsToDo)
{
if (worker.CancellationPending)
{
// Someone has asked us to stop doing our thing
break;
}
else
{
DoTheThing(thing);
}
}
};
Someone who wanted to cancel this BackgroundWorker, if it were running, would call
worker.CancelAsync();
which would cause it to set its CancellationPending property to true and cause the DoWork event handler that's currently running to break out of its loop after it finished processing the current thing. It wouldn't terminate immediately; the thing doing the work has to periodically ask "should I quit now or keep going?" Either way, the worker thread terminates gracefully.
The code written above tries to terminate the background worker immediately by aborting the thread. This is usually not the way that you want to do it. Ideally, this code should be changed so that the functions passed in as the doWork parameter regularly check for a cancellation request.
But the main reason that it doesn't work as written is that the currentThread member is not set until after the doWork function is invoked:
bw.DoWork += (sender, args) =>
{
if (doWork != null)
{
args.Result = (K)doWork((T)args.Argument);
currentThread = System.Threading.Thread.CurrentThread;
}
};
A call to StopImmediately() would see a null thread, because the DoWork callback is waiting for the function passed in as doWork to finish before it assigns the currentThread member.
They should be flipped as follows
bw.DoWork += (sender, args) =>
{
if (doWork != null)
{
currentThread = System.Threading.Thread.CurrentThread;
args.Result = (K)doWork((T)args.Argument);
}
};
and then it will indeed terminate uncleanly and (more or less) immediately. I recommend reading this MSDN article for an example of how to better approach supporting cancellation in a BackgroundWorker.
Hope this helps!

Related

Waiting for all Queued Backgroundworkers to finish?

I was trying out some QueuedBackgroundWorker class that I found here. It works well, except I'm wondering how I can wait till all queued workers have finished? For example, if the user goes to close the program I'd like the program to wait until all workers are finished and then close.
I tried doing something like this on the GUI thread, but it just seems to block:
try
{
while (myWorkerQueue.Queue.Count > 0) ;
}
catch (InvalidOperationException)
{
}
Also tried while(myWorkerQueue.Queue.Peek() != null) and got same result.
Code for 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
}
Do you absolutely have to use BackgroundWorker? .NET 4 introduces the Task API (aka Task Parallel Library or TPL in short): you can start several tasks and use Task.WhenAll to provide a continuation that only executes when all tasks are finished:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var myTasks = new List<Task<BitmapImage>>();
// Task<T>.Factory.StartNew starts the given method on a Thread Pool thread
myTasks.Add(Task<BitmapImage>.Factory.StartNew(LoadPicture1));
myTasks.Add(Task<BitmapImage>.Factory.StartNew(LoadPicture2));
// The important part: Task.WhenAll waits asynchronously until all tasks
// in the collection finished sucessfully. Only then, the lambda that is
// given to the ContinueWith method is executed. The UI thread does not block
// in this case.
Task.WhenAll(myTasks)
.ContinueWith(task =>
{
foreach (var bitmapImage in task.Result)
{
var image = new Image { Source = bitmapImage };
ImageStackPanel.Children.Add(image);
}
},
TaskScheduler.FromCurrentSynchronizationContext());
}
private BitmapImage LoadPicture1()
{
return LoadImageFile("Picture1.jpg");
}
private BitmapImage LoadPicture2()
{
// Simulate that this loading process takes a little bit longer
Thread.Sleep(1000);
return LoadImageFile("Picture2.jpg");
}
private BitmapImage LoadImageFile(string path)
{
using (var fileStream = new FileStream(path, FileMode.Open))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = fileStream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
}
You even can use async await if you program against .NET 4.5 (but I doubt that you use this version of .NET because you provided the .NET 4.0 tag in your question). Anyway, in case you want to stick to several BackgroundWorker objects, I would encapsulate all of them in a class and register to the Completed events of them. If all of them raised this event, then I would raise another event that tells all of them have completed.
You can learn more about the TPL here: https://msdn.microsoft.com/en-us/library/dd537609(v=vs.110).aspx
You can download the whole example that I created here: https://dl.dropboxusercontent.com/u/14810011/LoadSeveralItemsWithTasks.zip
If you do something like that
while (myWorkerQueue.Queue.Count > 0) ;
Your while loop is taking so much ressource that there is no more for your background threads. It appears blocked.
If you want to keep your while loop (which I don't advise), at least put a sleep in so your background threads can work:
while (myWorkerQueue.Queue.Count > 0)
System.Threading.Thread.Sleep(1000);
The easiest solution as you said in your comments is to hook the closing event and abort it if myWorkerQueue.Queue.Count > 0.
A more elegant solution is to create a modal form with a progress bar, show it when the form is closing and if myWorkerQueue.Queue.Count > 0, the progress bar would progress as the remaining background worker finish...

getting stackoverflow exception while passing value in different thread in c#

I am creating an opc server with third party dll.they had given an example in which all functions r running on a different thread.
here is the example,OPCServer.cs:
public static OpcServer CreateInstanceAsync()
{
Thread thread = new Thread(new ParameterizedThreadStart(InitializationThread));
OpcServer opcServer = new OpcServer();
thread.Start(opcServer);
thread.Join();
return opcServer;
}
static void InitializationThread(object arg)
{
((OpcServer)arg).Initialize();
}
void Initialize()
{
//some stuff
}
public void UpdateValues(string[] n)
{
this.BeginUpdate();
value1 = (object[])n;
for (int i = 0; i < tag_count; i++)
{
this.SetTag(tag_ids[i], value1[i], Quality.Good, FileTime.UtcNow);
}
this.EndUpdate(false);
}
I am getting problem in the method UpdateValues();
in the main form:
public Form1()
{
InitializeComponent();
opcServer = OpcServer.CreateInstanceAsync();
opcServer.UpdateValues(valuesInArray);
}
there is a timer & the UpdateValues() method will call at every time tick with a new value. interval is 10 secs.
private void timer1_Tick(object sender, EventArgs e)
{
opcServer.UpdateValues(valuesInArray);
}
the program is running smoothly for some time. but after that it showing stack overflow exception.,sometimes pc got hanged.i don't understand why? how do i get rid from this? the OPCServer.cs is given by the 3rd party.my work is to passing value in that particular method.will i have to create a new thread each time i will call that method?
Try BackgroundWorker for updating form while running long process. Use ProgressChanged event to update the form values else invoke a delegate to update form controls.
Another alternative would be to use the Task Parallel Library and then use events and delegates to interact with form elements.
Using the Task Parallel Library is very easy:
foreach (DriveInfo info in DriveInfo.GetDrives())
{
if (info.DriveType == DriveType.Fixed)
{
var task = Task.Factory.StartNew(() => scanFiles(findType, info.RootDirectory.Name));
}
}
This would be an example of interacting with the form elements:
In my external class:
/// <summary>
/// Delegate for setting text box text
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public delegate void TextBoxEventHandler(object sender, TextEventArgs e);
/// <summary>
/// Event for changing tool bar text
/// </summary>
public event TextBoxEventHandler ChangeTextBoxText = delegate { };
/// <summary>
/// Function that raises set tool bar text event
/// </summary>
/// <param name="s"></param>
public void SetTextBoxText(string s)
{
ChangeTextBoxText(this, new TextEventArgs(s));
}
In my form:
scanner.ChangeTextBoxText += scanner_ChangeTextBoxText;
private void scanner_ChangeTextBoxText(object sender, FS.TextEventArgs e)
{
addMessage(e.Message);
}
delegate void SetTextCallback(string text);
private void addMessage(string message)
{
if (edtContents.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(addMessage);
this.Invoke(d, new object[] { message });
}
else
{
edtContents.Text += String.Format("{0}{1}", message, Environment.NewLine);
edtContents.SelectionStart = edtContents.Text.Length;
edtContents.ScrollToCaret();
}
}
first of all why do you create a thread here
public static OpcServer CreateInstanceAsync()
{
Thread thread = new Thread(new ParameterizedThreadStart(InitializationThread));
OpcServer opcServer = new OpcServer();
thread.Start(opcServer);
thread.Join();
return opcServer;
}
because probably i think, you just don't want to hang your main form creation once you got the OpcServer object, you are just using the same instance to call the UpdateValues() in a timer.
now as you are piling things up in this call . how many updates you are adding.
this.SetTag(tag_ids[i], value1[i], Quality.Good, FileTime.UtcNow);
There must be some method to remove tags which are Old/Obsolete.
Check for the API documentation for freeing up the objects

why isn't possible to transfer ListA from Slave Thread to Main Thread?

I'm trying to generate and dispatch (while having my UI Thread with progressRing.IsActive = true;), three List Objects on a BackgroundWorker and then transfer said list to the UI Thread, but I'm running into issues with...
Must create DependencySource on same Thread as the DependencyObject.
Resources, I've read
C# lock (MSDN Documentation)
Sending Arguments to Background Worker (Stackoverflow)
C# Background Worker (MSDN Documentation)
C# getting Background Worker to return a result (Stackoverflow)
Method BackgroundLogin() of Partial Class MainWindow
private void BackgroundLogin()
{
//process the form on UI Thread
this.LoginForm(FadeOut);
this.LoadingRing(FadeIn);
//Start a new Thread
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
//initialize custom class WorkerThread object to store/process a result
WorkerThread wt = new WorkerThread(this, txtEmailAddress.Text, txtPassword.Password);
//start the worker and send the object across.
worker.RunWorkerAsync(wt);
}
Method worker_DoWork of Partial Class MainWindow
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
//grab the object
WorkerThread wt = (WorkerThread)e.Argument;
//check if we can login
if (!wt.Login())
{
//cancel the thread
e.Cancel = true;
}
else
{
//load additional data
wt.UpdateAuthLbl(".. Loading New Data ..");
wt.LoadLists();
wt.UpdateAuthLbl(".. Data Loaded ..");
}
//pass the object back
e.Result = wt;
}
Method loadLists() of Class WorkerThread
/// <summary>
/// Load data into the list
/// </summary>
public void LoadLists()
{
this.gene_fact_list = db.loadGeneFactTable();
this.gene_trait_fact_list = db.loadGeneTraitFactTable(this.gene_fact_list);
this.category_trait_list = db.loadCategoryTraits();
}
Method worker_RunWorkerCompleted of Partial Class MainWindow, Object gl of Class GeneList
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//grab the finished object
WorkerThread wt = (WorkerThread) e.Result;
//work out if we are logged in
Boolean LoginFlag = !e.Cancelled && e.Error == null;
if (LoginFlag)
{
lblAuthentication.Content = ".. Loading Interface ..";
//pass the finished object into memory
this.gl = wt;
//reset the listbox
this.resetListBox();
}
this.LoginForm(LoginFlag);
}
Method resetListBox() and ListBoxItems
/// <summary>
/// Load the data for the form
/// </summary>
public void resetListBox()
{
if (this.gl.notNullOrEmpty())
{
this.ListBoxItems.Clear();
//begin compiling the mainTab
foreach (KeyValuePair<long, GeneNotesDataModel> kvp in this.gl.gene_fact_list)
{
this.ListBoxItems.Add(kvp.Value);
}
}
} //close function
//declare WPF list binding
private ObservableCollection<GeneNotesDataModel> _listBoxItems = new ObservableCollection<GeneNotesDataModel>();
/// <summary>
/// Control the listbox of rsid codes
/// </summary>
public ObservableCollection<GeneNotesDataModel> ListBoxItems
{
get { return _listBoxItems; }
set { _listBoxItems = value; }
}
XAML ListBox lstSnpCodes
<ListBox ItemsSource="{Binding ElementName=UI, Path=ListBoxItems}" Margin="6,38,0,60" BorderThickness="2" HorizontalAlignment="Left" Width="180" Name="lstSnpCodes" SelectionChanged="lstSnpCodes_SelectionChanged" KeyUp="OnKeyUpHandler" />
The line this.ListBoxItems.Add(kvp.Value); causes the Exception to occur (If I replace it with Debug.WriteLine(kvp.Value.getValueAsString()); it will run just fine). Any ideas on why I'm getting a DependencySource exception? and why isn't possible to transfer ListA from Slave Thread to Main Thread?
PasteBin Links expire in April 2013
Instead of trying to access the property directly across threads, try accessing the variable behind it.
In sample app:
private static List<object> _lst;
static void Main(string[] args)
{
Task tsk = new Task(() => _lst = new List<object>()); //task will create new thread if available and execute the Action.
tsk.Start(); //initiates task, which initiates new List<object> on new thread.
tsk.Wait(); //waits for task to complete. Note that this locks main thread.
_lst.Add(1); //Attempt to access _lst from main thread.
}
Specific to your example.
private ReadOnly object _lockMyList = new object();
private List _MyList;
public void SetMyList(List lst) //Use this to set list from other thread.
{
Lock(_lockMyList)
{
_MyList = lst;
}
//Invoke Property Changed event if needed for binding.
}
public List MyList
{
get
{
List lst = null;
Lock(_lockMyList)
{
lst = _MyList; //You might need to do _MyList.ToList() for true thread safety
}
return lst;
}
//Property setter if needed, but use accessor method instead for setting property from other thread
}
Also, you may want to look into SynchronizedCollection to see if it is more suitable for your needs.
I hope this helps.

Need to template for worker thread method

I need to design perfect worker thread method. The method must do the following:
1) extract something from queue (let's say a queue of string) and do something
2) stop and return when class is disposed
3) wait for some event (that queue is not empty) and do not consume cpu
4) run in separate thread
Main thread will add string to queue and signal thread method to continue and do the job.
I would like you to provide me the the template with required syncronization objects.
class MyClass, IDisposable
{
// Thread safe queue from third party
private ThreadSafeQueue<string> _workerQueue;
private Thread _workerThread;
public bool Initialize()
{
_workerThread = new Thread(WorkerThread).Start();
}
public AddTask(string object)
{
_workerQueue.Enqueue(object);
// now we must signal worker thread
}
// this is worker thread
private void WorkerThread()
{
// This is what worker thread must do
List<string> objectList = _workerQueue.EnqueAll
// Do something
}
// Yeap, this is Dispose
public bool Dispose()
{
}
}
Try something like this. instantiate with type string and give it a delegate to process your string:
public class SuperQueue<T> : IDisposable where T : class
{
readonly object _locker = new object();
readonly List<Thread> _workers;
readonly Queue<T> _taskQueue = new Queue<T>();
readonly Action<T> _dequeueAction;
/// <summary>
/// Initializes a new instance of the <see cref="SuperQueue{T}"/> class.
/// </summary>
/// <param name="workerCount">The worker count.</param>
/// <param name="dequeueAction">The dequeue action.</param>
public SuperQueue(int workerCount, Action<T> dequeueAction)
{
_dequeueAction = dequeueAction;
_workers = new List<Thread>(workerCount);
// Create and start a separate thread for each worker
for (int i = 0; i < workerCount; i++)
{
Thread t = new Thread(Consume) { IsBackground = true, Name = string.Format("SuperQueue worker {0}",i )};
_workers.Add(t);
t.Start();
}
}
/// <summary>
/// Enqueues the task.
/// </summary>
/// <param name="task">The task.</param>
public void EnqueueTask(T task)
{
lock (_locker)
{
_taskQueue.Enqueue(task);
Monitor.PulseAll(_locker);
}
}
/// <summary>
/// Consumes this instance.
/// </summary>
void Consume()
{
while (true)
{
T item;
lock (_locker)
{
while (_taskQueue.Count == 0) Monitor.Wait(_locker);
item = _taskQueue.Dequeue();
}
if (item == null) return;
// run actual method
_dequeueAction(item);
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
// Enqueue one null task per worker to make each exit.
_workers.ForEach(thread => EnqueueTask(null));
_workers.ForEach(thread => thread.Join());
}
}
What you are describing is best accomplished with the producer-consumer pattern. This pattern is most easily implemented with a blocking queue. If you are using .NET 4.0 then you can take advantage of the BlockingCollection class. Here is how I am seeing your code working. In the following example I am using a null value as sentinel for gracefully ending the consumer, but you could also take advantage of the CancellationToken parameter on the Take method.
public class MyClass : IDisposable
{
private BlockingCollection<string> m_Queue = new BlockingCollection<string>();
public class MyClass()
{
var thread = new Thread(Process);
thread.IsBackground = true;
thread.Start();
}
public void Dispose()
{
m_Queue.Add(null);
}
public void AddTask(string item)
{
if (item == null)
{
throw new ArgumentNullException();
}
m_Queue.Add(item);
}
private void Process()
{
while (true)
{
string item = m_Queue.Take();
if (item == null)
{
break; // Gracefully end the consumer thread.
}
else
{
// Process the item here.
}
}
}
}
I think you should consider using BackgroundWorker class, which may fit well to your needs.
Sounds like BlockingQueue is what you need.
You should take a look at the new .Net 4 System.Collections.Concurrent Namespace. Also this little example should help you to get a better understanding on how to use it.

How do I wait for a C# event to be raised?

I have a Sender class that sends a Message on a IChannel:
public class MessageEventArgs : EventArgs {
public Message Message { get; private set; }
public MessageEventArgs(Message m) { Message = m; }
}
public interface IChannel {
public event EventHandler<MessageEventArgs> MessageReceived;
void Send(Message m);
}
public class Sender {
public const int MaxWaitInMs = 5000;
private IChannel _c = ...;
public Message Send(Message m) {
_c.Send(m);
// wait for MaxWaitInMs to get an event from _c.MessageReceived
// return the message or null if no message was received in response
}
}
When we send messages, the IChannel sometimes gives a response depending on what kind of Message was sent by raising the MessageReceived event. The event arguments contain the message of interest.
I want Sender.Send() method to wait for a short time to see if this event is raised. If so, I'll return its MessageEventArgs.Message property. If not, I return a null Message.
How can I wait in this way? I'd prefer not to have do the threading legwork with ManualResetEvents and such, so sticking to regular events would be optimal for me.
Use a AutoResetEvent.
Gimme a few minutes and I'll throw together a sample.
Here it is:
public class Sender
{
public static readonly TimeSpan MaxWait = TimeSpan.FromMilliseconds(5000);
private IChannel _c;
private AutoResetEvent _messageReceived;
public Sender()
{
// initialize _c
this._messageReceived = new AutoResetEvent(false);
this._c.MessageReceived += this.MessageReceived;
}
public Message Send(Message m)
{
this._c.Send(m);
// wait for MaxWaitInMs to get an event from _c.MessageReceived
// return the message or null if no message was received in response
// This will wait for up to 5000 ms, then throw an exception.
this._messageReceived.WaitOne(MaxWait);
return null;
}
public void MessageReceived(object sender, MessageEventArgs e)
{
//Do whatever you need to do with the message
this._messageReceived.Set();
}
}
Have you tried assigning the function to call asynchronously to a delegate, then invoking the mydelegateinstance.BeginInvoke?
Linky for reference.
With the below example, just call
FillDataSet(ref table, ref dataset);
and it'll work as if by magic. :)
#region DataSet manipulation
///<summary>Fills a the distance table of a dataset</summary>
private void FillDataSet(ref DistanceDataTableAdapter taD, ref MyDataSet ds) {
using (var myMRE = new ManualResetEventSlim(false)) {
ds.EnforceConstraints = false;
ds.Distance.BeginLoadData();
Func<DistanceDataTable, int> distanceFill = taD.Fill;
distanceFill.BeginInvoke(ds.Distance, FillCallback<DistanceDataTable>, new object[] { distanceFill, myMRE });
WaitHandle.WaitAll(new []{ myMRE.WaitHandle });
ds.Distance.EndLoadData();
ds.EnforceConstraints = true;
}
}
/// <summary>
/// Callback used when filling a table asynchronously.
/// </summary>
/// <param name="result">Represents the status of the asynchronous operation.</param>
private void FillCallback<MyDataTable>(IAsyncResult result) where MyDataTable: DataTable {
var state = result.AsyncState as object[];
Debug.Assert((state != null) && (state.Length == 2), "State variable is either null or an invalid number of parameters were passed.");
var fillFunc = state[0] as Func<MyDataTable, int>;
var mre = state[1] as ManualResetEventSlim;
Debug.Assert((mre != null) && (fillFunc != null));
int rowsAffected = fillFunc.EndInvoke(result);
Debug.WriteLine(" Rows: " + rowsAffected.ToString());
mre.Set();
}
Perhaps your MessageReceived method should simply flag a value to a property of your IChannel interface, while implementing the INotifyPropertyChanged event handler, so that you would be advised when the property is changed.
By doing so, your Sender class could loop until the max waiting time is elapsed, or whenever the PropertyChanged event handler occurs, breaking the loop succesfully. If your loop doesn't get broken, then the message shall be considered as never received.
Useful sample with AutoResetEvent:
using System;
using System.Threading;
class WaitOne
{
static AutoResetEvent autoEvent = new AutoResetEvent(false);
static void Main()
{
Console.WriteLine("Main starting.");
ThreadPool.QueueUserWorkItem(
new WaitCallback(WorkMethod), autoEvent);
// Wait for work method to signal.
autoEvent.WaitOne();
Console.WriteLine("Work method signaled.\nMain ending.");
}
static void WorkMethod(object stateInfo)
{
Console.WriteLine("Work starting.");
// Simulate time spent working.
Thread.Sleep(new Random().Next(100, 2000));
// Signal that work is finished.
Console.WriteLine("Work ending.");
((AutoResetEvent)stateInfo).Set();
}
}
WaitOne is really the right tool for this job. In short, you want to wait between 0 and MaxWaitInMs milliseconds for a job to complete. You really have two choices, poll for completion or synchronize the threads with some construct that can wait an arbitrary amount of time.
Since you're well aware of the right way to do this, for posterity I'll post the polling version:
MessageEventArgs msgArgs = null;
var callback = (object o, MessageEventArgs args) => {
msgArgs = args;
};
_c.MessageReceived += callback;
_c.Send(m);
int msLeft = MaxWaitInMs;
while (msgArgs == null || msLeft >= 0) {
Thread.Sleep(100);
msLeft -= 100; // you should measure this instead with say, Stopwatch
}
_c.MessageRecieved -= callback;

Categories

Resources