Turn asynchronous calls into synchronous

2019-03-27 07:16发布

Is there any good practice (pattern) in turning asynchronous calls into synchronous?
I have a third party library who's methods are all asynchronos, to get result of almoust any method you must listen to an event, which will bring some context with it. basically it looks like:

service.BeginSomething(...);
service.OnBeginSomethingCompleted += ;

what I need is to execute some code after BeginSomething when it is really complete (thus after OnBeginSomethingCompleted is triggered). It is very inconvinient to handle the response in the event.

The only way I could think of is running a Thread.Sleep loop and wait till some field on the form is updated, but it doesn't look like very elegant sollution.

I'm using .net 4.0.

7条回答
孤傲高冷的网名
2楼-- · 2019-03-27 07:59

As other said, if possible you should try to make your own code async. If that won't work, does your third-party library support the standard BeginXXX, EndXXX async pattern? If so, then using the TPL would make things easy for you. Your code will look something like this:

using System.Threading.Tasks;

...

var task = Task<TResult>.Factory.FromAsync(
    service.BeginSomething, service.EndSomething, arg1, arg2, ..., null);

task.Wait();
var result = task.Result;

The specific overload you'll want to use will depend on how many parameters you need to pass. You can see the list here.

查看更多
闹够了就滚
3楼-- · 2019-03-27 08:05

You might want to look at Reactive Extensions

With Rx you can wrap that into an 'event' basically - the do something like someClass.SomeEvent.Subscribe(d=>...) to subscribe using usually some lambda expression to handle what you need. Also use ObserveOn to handle it on the GUI thread (see the details, this is just a hint).

Other option is to use async await (which is now available for use with VS 2010).

hope this helps

NOTE: Rx have a native support for async methods and turning them into Rx events with pretty much just one call. Take a look at Observable.FromAsyncPattern FromAsyncPattern

查看更多
趁早两清
4楼-- · 2019-03-27 08:06

Use a ManualResetEvent. In your sync wrapper create it, then pass it to the service.BeginSomething() call as part of the state object. Immediately after the call, WaitOne() on it, this will block.

In the service.OnBeginSomethingCompleted event extract it from the state object and set it, this will unblock the sync caller.

查看更多
We Are One
5楼-- · 2019-03-27 08:12

The general trend of modern software development (on Windows platform too) is to run, what is possible asynchroniously.

Actually from Windows8 software design guidelines, if the code runs more then 50ms, it has to be asynchronious.

So I would not suggest to block the thread, but instead benefit from that library and provide to the user with some nice looking animation saying "wait, responce comming", or something like this, or some progress bar.

In short, do not block thread, notify a user about what is going on in app and leave it async.

查看更多
时光不老,我们不散
6楼-- · 2019-03-27 08:13

If BeginSomething() returns an IAsyncResult (like a delegate's .BeginInvoke would do), you can get the WaitHandle from that:

service.OnBeginSomethingCompleted += ;
var asyncResult = service.BeginSomething();
asyncResult.AsyncWaitHandle.WaitOne(); // Blocks until process is complete

By the way, by assigning the event handler after starting the async process, you are introducing a race condition where the async call may complete before the event is registered, causing it to never fire.

查看更多
beautiful°
7楼-- · 2019-03-27 08:19

This solution is similar to Brian Gideon's, but I think a little bit cleaner for what you're trying to do. It uses the Monitor object to cause the calling thread to wait until the Completed event is triggered.

public class SomeClass : BaseClass
{
   public void ExecuteSomethingAndWaitTillDone()
    {
        // Set up the handler to signal when we're done
        service.OnBeginSomethingCompleted += OnCompleted;

        // Invoke the asynchronous method.
        service.BeginSomething(...);

            // Now wait until the event occurs
            lock (_synchRoot)
            {
                // This waits until Monitor.Pulse is called
                Monitor.Wait(_synchRoot);
            }
    }

    // This handler is called when BeginSomething completes
    private void OnCompleted(object source, ...)
    {
        // Signal to the original thread that it can continue
        lock (_synchRoot)
        {
            // This lets execution continue on the original thread
            Monitor.Pulse(_synchRoot);
        }
    }

    private readonly Object _synchRoot = new Object();
}
查看更多
登录 后发表回答