I am using Func<> or Action<>.BeginInvoke to run methods asynchronously using the ThreadPool.
Is it possible to have the AsyncCallback invoke a function (or an event, technically) on the original thread that spawned the new thread?
I know that in WinForms apps you can use Control / ISynchronizeInvoke / Dispatcher to alert the UI thread when async operations complete — but this isn’t a WinForms app and it does not seem to work.
class SyncTest : System.ComponentModel.ISynchronizeInvoke
{
public void TestMethod() {
Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId + " Test Method Fired.");
}
public IAsyncResult BeginInvoke(Delegate method, object[] args) {
throw new NotImplementedException();
}
public object EndInvoke(IAsyncResult result) {
throw new NotImplementedException();
}
public object Invoke(Delegate method, object[] args) {
return method.DynamicInvoke(args);
}
public bool InvokeRequired {
get { throw new NotImplementedException(); }
}
public void Test()
{
var sleep = new Action(() => System.Threading.Thread.Sleep(5000));
System.ComponentModel.ISynchronizeInvoke originalThreadCallback = (System.ComponentModel.ISynchronizeInvoke)this;
for (int i = 0; i < 5; i++)
{
sleep.BeginInvoke(new AsyncCallback(res =>
{
(res.AsyncState as Action).EndInvoke(res);
Console.WriteLine("Thread inside callback: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
originalThreadCallback.Invoke(new Action(() => this.TestMethod()), null);
}), sleep);
}
}
}
The output from the Test() method is as follows:
Main thread = 9
TEST DONE
Thread inside callback: 11
Thread inside callback: 10
10 Test Method Fired.
11 Test Method Fired.
Thread inside callback: 12
12 Test Method Fired.
Thread inside callback: 13
13 Test Method Fired.
Thread inside callback: 14
14 Test Method Fired.
As you can see nothing is invoked on the original thread with ID = 9.
Obviously my implementation of ISynchronizeInvoke isn’t actually doing anything to invoke the Test Method on the original thread, and thats the problem — but I also can’t seem to get an ISynchronizeInvoke instance out of any of my delegates or events (it is always null). What objects in .NET 4 implement the interface correctly?
Thanks.
Well, you’d have to have some sort of loop, just like Windows Forms (etc) do, waiting for work to process.
You could write that yourself, but it would mean your original thread would have to be kept relatively free, just like you don’t want to block the UI thread in a Windows Forms app. Basically, you’d be rewriting some of the infrastructure that WinForms uses. There’s nothing I’m aware of that just does that out of the box, although the .NET 4 producer/consumer queue classes (e.g.
BlockingCollection<T>) would at least help.The docs for
BlockingCollection<T>give a quick example of how to implement a producer/consumer queue – basically your threadpool threads would callAddon the queue, and your consumer thread would callTakeon it (orTryTake).There’s also a Channel-9 video with expert Stephen Toub (of the parallel team in MS) about using
BlockingCollection.