Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a generic way to synchronize an asynchronous method?

We have this common scenario where we have a method that performs some action asyncronously and raises an event when it's done.

There are times where we want it done synchronously instead so we have code that looks similar to this:

ManualResetEvent reset = new ManualResetEvent(false);
someobject.AsyncActionDone += (sender, args) => reset.Set();
someobject.PerformAsyncAction();
reset.WaitOne();

Is there a way to write a helper method to do this? I can pass in the Action to perform, but I'm not sure how to pass in something that lets the helper method know which event to listen to since it doesn't look like you can pass in an EventHandler as a parameter.

Preferably a solution that doesn't require reflection

There seems to be some confusion, this is a sample of what someobject's class is like:

public class SomeClass
{
    private ExternalServer someServerOverTheNetwork = new ExternalServer();

    public event EventHandler AsyncActionDone;
    public Data SomeData { get; set; }
    public void PerformAsyncAction()
    {
        someServerOverTheNetwork.GetSomeData(OnDataRetrived);
    }

    public Data OnDataRetrived(Data someData)
    {
        AsyncActionDone(this, new DataEventArgs(someData));
    }
}
like image 673
Davy8 Avatar asked Feb 12 '10 22:02

Davy8


1 Answers

I would consider implementing the Asynchronous Design Pattern in the objects that performs asynchronous operation.

public object Operation(object arg)
{
    var ar = BeginOperation(arg, null, null);

    return EndOperation(ar);
}

public IAsyncResult BeginOperation(object arg, AsyncCallback asyncCallback, object state)
{
    AsyncResult asyncResult = new AsyncResult(asyncCallback, state);

    // Lauch the asynchronous operation

    return asyncResult;
}

private void LaunchOperation(AsyncResult asyncResult)
{
    // Do something asynchronously and call OnOperationFinished when finished
}

private void OnOperationFinished(AsyncResult asyncResult, object result)
{
    asyncResult.Complete(result);
}


public object EndOperation(IAsyncResult asyncResult)
{
    AsyncResult ar = (AsyncResult)asyncResult;

    return ar.EndInvoke();
}

With this pattern you have the flexibility of having multiple concurrent asynchronous operation on your object.

Note: You can easily find an implementation of a generic AsyncResult class on the web.

Edit:

As you want to keep the current design, if all your object can only have one asynchronous operation, then you could define an IAsyncOperation interface and implement it in all your object.

public interface IAsyncOperation
{
    event EventHandler AsyncActionDone;
    void PerformAsyncAction();
}

Then you could have:

public static CallSynchronously(IAsyncOperation asyncOperation)
{
    ManualResetEvent reset = new ManualResetEvent(false);
    asyncOperation.AsyncActionDone += (sender, args) => reset.Set();
    asyncOperation.PerformAsyncAction();
    reset.WaitOne();
}

If your objects can contain multiple asynchronous operation, then without reflection I think there is no way to achieve what you want to do, but you could still define a synchronous version of all asynchronous operation that wraps the ManualResetEvent.

public void PerformAction()
{
    ManualResetEvent reset = new ManualResetEvent(false);
    this.AsyncActionDone += (sender, args) => reset.Set();
    this.PerformAsyncAction();
    reset.WaitOne();
}
like image 78
Jeff Cyr Avatar answered Sep 20 '22 12:09

Jeff Cyr