views:

69

answers:

5

I'm trying to wrap my head around asynchronous calling of methods.

public IDictionary<string, int> DoSomething(string startsWith)
{
    return new Dictionary<string, int>();
}

public IAsyncResult BeginDoSomething(string startsWith, AsyncCallback callback,
                                     object state)
{
    return new Func<string, IDictionary<string, int>>(DoSomething)
           .BeginInvoke(startsWith, callback, state);
}

public IDictionary<string, int> EndDoSomething(IAsyncResult result)
{
    // How to return the IDictionary?!
}

My problem is that I have no idea how to get the IDictionary in the EndDoSomething method. I googled around and saw that some people use the state and callback, while others create their own class that derives from IAsyncResult, return it from Begin and cast to it in End.

Is that really the proper way to do it? Or what would be the proper way?

A: 

I think you should be looking at this in terms of invoking a synchronous method asynchronously through a delegate.

http://msdn.microsoft.com/en-us/library/22t547yb(v=VS.71).aspx

Steven Sudit
+1  A: 

A little unpleasant looking, but this should do it (not explicitly tested):

public IDictionary<string, int> DoSomething(string startsWith)
{
    return new Dictionary<string, int>();
}

public IAsyncResult BeginDoSomething(string startsWith, AsyncCallback callback,
                                     object state)
{
    var doSomething = new Func<string, IDictionary<string, int>>(DoSomething);

    return doSomething.BeginInvoke(startsWith, callback, new object[] { doSomething, state });
}

public IDictionary<string, int> EndDoSomething(IAsyncResult result)
{
    var doSomething = (Func<string, IDictionary<string, int>>)((object[])result.AsyncState)[0];

    return doSomething.EndInvoke(result);
}
Jesse C. Slicer
A: 

you need to create a delegate and call that delegate asynchronously.

public delegate IDictionary<String, Int> MyDelegate(string s);
MyDelegate delObj = new MyDelegate(DoSomething);
string str = "testString";
delObj.BeginInvoke(str, new AsyncCallback(CallbackMethod), null);

and

private void CallbackMethod(IAsyncResult iresult)
{
//logic
AsyncResult iResult = iresult as AsyncResult;
MyDelegate del = iResult.AsyncDelegate as MyDelegate;
IDictionary<string,int> result = del.EndInvoke(iresult);
}

http://www.codeproject.com/KB/cs/AsyncMethodInvocation.aspx

not tested it, you can try it out and let me know Thanks

Tiju John
A: 

There are 2 threads that would be running: (1) MainThread and (2)the worker thread that runs DoSomething. Thread (2) is going to call your callback, so it cannot return anything. Instead, the callback should retrieve the result and put it in a field.

The main thread can then check the value of that field.

Here's an example:

using System;
using System.Collections.Generic;
using System.Threading;

namespace Commons.CLI.Sandbox
{
    class Program
    {
        static void Main(string[] args)
        {
            var myMain = new Program();
            myMain.Run();

            Console.WriteLine("Result: " + myMain.Output["start"] + " on thread: " + Thread.CurrentThread.ManagedThreadId);
            Console.ReadKey();
        }

        public IDictionary<string, int> Output { get; set; }
        private object _outputLock = new object();

        private DoSomethingResultRetriever _methodToCall;

        private ManualResetEvent _waiter;

        public void Run()
        {
            _waiter = new ManualResetEvent(false);

            _methodToCall = DoSomething;
            var asyncResult = BeginDoSomething("start");

            // We need to wait for the other thread to run
            Console.WriteLine(String.Format("Thread {0} is about to wait.", Thread.CurrentThread.ManagedThreadId));

            // Do other things ...

            if (asyncResult.IsCompleted) return;

            _waiter.WaitOne();
        }

        private IAsyncResult BeginDoSomething(string startsWith)
        {
            return _methodToCall.BeginInvoke(startsWith, EndDoSomething, null);
        }

        private void EndDoSomething(IAsyncResult ar)
        {
            lock (_outputLock)
            {
                Output = _methodToCall.EndInvoke(ar);
            }

            _waiter.Set();
        }

        private delegate IDictionary<string, int> DoSomethingResultRetriever(string startsWith);

        private IDictionary<string, int> DoSomething(string startsWith)
        {
            Console.WriteLine("DoSomething on thread: " + Thread.CurrentThread.ManagedThreadId);
            return new Dictionary<string, int>() { { startsWith, 10 } };
        }
    }
}
rgunawan
A: 

I think the easiest way is to use the Task<TResult> class (added in .NET 4), which inherits from IAsyncResult:

public IDictionary<string, int> DoSomething(string startsWith) 
{ 
  return new Dictionary<string, int>(); 
} 

public IAsyncResult BeginDoSomething(string startsWith, AsyncCallback callback, 
                                     object state) 
{
  return Task.Factory.StartNew(_ => { return DoSomething(startsWith); }, state);
} 

public IDictionary<string, int> EndDoSomething(IAsyncResult result) 
{
  var task = (Task<IDictionary<string, int>>)result;
  return task.Result;
} 

Not only does Task<TResult> implement IAsyncResult, but it also handles marshaling any errors to the caller; they are raised automatically by accessing Task<TResult>.Result.

Stephen Cleary
Yes, this does look better than BeginInvoke with a delegate.
Steven Sudit