Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapping an asynchronous method synchronously in C#

I have a third party library containing a class which performs a function asynchronously. The class inherits from the Form. The function basically performs a calculation based on data stored in a database. Once it has finished, it calls a _Complete event in the calling form.

What I would like to do is call the function synchronously but from a non-windows form application. The problem is, no matter what I do, my application blocks and the _Complete event handler never fires. From a windows form I can simulate the function running synchronously by using a "complete" flag and a "while (!complete) application.doevents", but obviously application.doevents isnt available in a non-windows form application.

Is there something that would stop me using the class's method outside of a windows form application (due to it inheriting from 'Form') ? Is there some way I can work around this ?

Thanks, Mike

like image 889
mikecamimo Avatar asked Jan 24 '09 06:01

mikecamimo


People also ask

Should I expose asynchronous wrappers for synchronous methods?

Asynchronous methods should not be exposed purely for the purpose of offloading: such benefits can easily be achieved by the consumer of synchronous methods using functionality specifically geared towards working with synchronous methods asynchronously, e.g. Task.

How do we call synchronous method from asynchronous method in C?

The simplest way to execute a method asynchronously is to start executing the method by calling the delegate's BeginInvoke method, do some work on the main thread, and then call the delegate's EndInvoke method. EndInvoke might block the calling thread because it does not return until the asynchronous call completes.

Can we call asynchronous method from another synchronous method?

Solution A If you have a simple asynchronous method that doesn't need to synchronize back to its context, then you can use Task. WaitAndUnwrapException : var task = MyAsyncMethod(); var result = task. WaitAndUnwrapException();

Do async functions run synchronously?

Synchronous code inside an async function runs synchronously. The interpreter will only move on to the next line after the call of the async function (here, the console. log("b"); ) after all synchronous code has finished - for example, if it runs into an await .


3 Answers

At a stab it might be worth trying something like the following which uses a WaitHandle to block the current thread rather than spinning and checking a flag.

using System;
using System.Threading;

class Program
{
    AutoResetEvent _autoEvent;

    static void Main()
    {
        Program p = new Program();
        p.RunWidget();
    }

    public Program()
    {
        _autoEvent = new AutoResetEvent(false);
    }

    public void RunWidget()
    {
        ThirdParty widget = new ThirdParty();           
        widget.Completed += new EventHandler(this.Widget_Completed);
        widget.DoWork();

        // Waits for signal that work is done
        _autoEvent.WaitOne();
    }

    // Assumes that some kind of args are passed by the event
    public void Widget_Completed(object sender, EventArgs e)
    {
        _autoEvent.Set();
    }
}
like image 165
Kev Avatar answered Oct 29 '22 03:10

Kev


I've got some more information on this problem (I'm working in the same team as mikecamimo).

The problem also occurs in the Windows Forms application, when replicated correctly. In the original OP, the problem didn't occur in the windows form because there was no blocking. When blocking is introduced by using a ResetEvent, the same problem occurs.

This is because the event handler (Widget_Completed) is on the same thread as the method calling Widget.DoWork. The result that AutoResetEvent.WaitOne(); blocks forever because the event handler is never called to Set the event.

In a windows forms environment this can worked around by using Application.DoEvents to poll the message queue and allow the event the be handled. See below.

using System;
using System.Threading;
using System.Windows.Forms;

class Program
{
    EventArgs data;

    static void Main()
    {
        Program p = new Program();
        p.RunWidget();
    }

    public Program()
    {
        _autoEvent = new AutoResetEvent(false);
    }

    public void RunWidget()
    {
        ThirdParty widget = new ThirdParty();                   
        widget.Completed += new EventHandler(this.Widget_Completed);
        data = null;
        widget.DoWork();

        while (data == null);
            Application.DoEvents();

        // do stuff with the results of DoWork that are contained in EventArgs.
    }

    // Assumes that some kind of args are passed by the event
    public void Widget_Completed(object sender, EventArgs e)
    {
        data = e;
    }
}

In a non windows forms application, such as a Windows Service, Application is not available so DoEvents cannot be called.

The problem is one of threading and that widget.DoWork's associated event handler somehow needs to be on another thread. This should prevent AutoResetEvent.WaitOne from blocking indefinitely. I think... :)

Any ideas on how to accomplish this would be fantastic.

like image 21
Rob Gray Avatar answered Oct 29 '22 03:10

Rob Gray


AutoResetEvent _autoEvent = new AutoResetEvent(false);

public  WebBrowser SyncronNavigation(string url)
{
    WebBrowser wb  = null;

    wb = new WebBrowser();
    wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);

    wb.ScriptErrorsSuppressed = true;
    wb.Navigate(new Uri(url));

    while (!_autoEvent.WaitOne(100))
        Application.DoEvents();

    return wb;
}

void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    //throw new NotImplementedException();
    _autoEvent.Set();
}
like image 21
user1619860 Avatar answered Oct 29 '22 03:10

user1619860