Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The easiest way to implement a synchronous method asynchronously is to put it into a delegate, and use the <code>BeginInvoke</code> and <code>EndInvoke</code> methods on the resulting delegate. This will run the synchronous method on a threadpool thread, and <code>BeginInvoke</code> will return an <code>IAsyncResult</code> implementation, so you don't have to implement the guts of it. However, you do need to smuggle a little extra data into the <code>IAsyncResult</code> returned by <code>IOperationInvoker.InvokeEnd</code>. You could do that easily by creating an implementation of <code>IAsyncResult</code> that delegates everything to an inner <code>IAsyncResult</code>, but has an extra field to contain the delegate, so that when the <code>IAsyncResult</code> instance is passed to <code>InvokeEnd</code>, you can access the delegate to call <code>EndInvoke</code> on it.</p> <p>However, after closer reading of your question, I see that you need to use an explicit thread with COM settings etc.</p> <p>What you need to do is properly implement <code>IAsyncResult</code>. Almost everything follows from this, since the <code>IAsyncResult</code> will contain all the bits needed for synchronization.</p> <p>Here's a very simple, but not terribly efficient, implementation of <code>IAsyncResult</code>. It encapsulates all the essential features: passing arguments, a synchronization event, callback implementation, propagating exceptions from async task and returning result.</p> <pre><code>using System; using System.Threading; class MyAsyncResult : IAsyncResult { object _state; object _lock = new object(); ManualResetEvent _doneEvent = new ManualResetEvent(false); AsyncCallback _callback; Exception _ex; bool _done; int _result; int _x; public MyAsyncResult(int x, AsyncCallback callback, object state) { _callback = callback; _state = state; _x = x; // arbitrary argument(s) } public int X { get { return _x; } } public void SignalDone(int result) { lock (_lock) { _result = result; _done = true; _doneEvent.Set(); } // never invoke any delegate while holding a lock if (_callback != null) _callback(this); } public void SignalException(Exception ex) { lock (_lock) { _ex = ex; _done = true; _doneEvent.Set(); } if (_callback != null) _callback(this); } public object AsyncState { get { return _state; } } public WaitHandle AsyncWaitHandle { get { return _doneEvent; } } public bool CompletedSynchronously { get { return false; } } public int Result { // lock (or volatile, complex to explain) needed // for memory model problems. get { lock (_lock) { if (_ex != null) throw _ex; return _result; } } } public bool IsCompleted { get { lock (_lock) return _done; } } } class Program { static void MyTask(object param) { MyAsyncResult ar = (MyAsyncResult) param; try { int x = ar.X; Thread.Sleep(1000); // simulate lengthy work ar.SignalDone(x * 2); // demo work = double X } catch (Exception ex) { ar.SignalException(ex); } } static IAsyncResult Begin(int x, AsyncCallback callback, object state) { Thread th = new Thread(MyTask); MyAsyncResult ar = new MyAsyncResult(x, callback, state); th.Start(ar); return ar; } static int End(IAsyncResult ar) { MyAsyncResult mar = (MyAsyncResult) ar; mar.AsyncWaitHandle.WaitOne(); return mar.Result; // will throw exception if one // occurred in background task } static void Main(string[] args) { // demo calling code // we don't need state or callback for demo IAsyncResult ar = Begin(42, null, null); int result = End(ar); Console.WriteLine(result); Console.ReadLine(); } } </code></pre> <p>It's important for correctness that client code can't see the <code>IAsyncResult</code> implementation, otherwise they might access methods like <code>SignalException</code> inappropriately or read <code>Result</code> prematurely. The class can be made more efficient by not constructing the <code>WaitHandle</code> implementation (<code>ManualResetEvent</code> in the example) if it's not necessary, but this is tricky to get 100% right. Also, the <code>Thread</code> and <code>ManualResetEvent</code> can and should be disposed of in the <code>End</code> implementation, as should be done with all objects that implement <code>IDisposable</code>. And obviously, <code>End</code> should check to make sure that it has gotten an implementation of the right class to get a nicer exception than a cast exception. I've left these and other details out as they obscure the essential mechanics of the async implementation.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload