在C#中同步包装异步方法

时间:2009-01-24 06:20:00

标签: c# asynchronous wrapper synchronous

我有一个第三方库,其中包含一个异步执行函数的类。该类继承自Form。该功能基本上基于存储在数据库中的数据执行计算。完成后,它会在调用表单中调用_Complete事件。

我想要做的是同步调用该函数,但是从非Windows窗体应用程序调用。问题是,无论我做什么,我的应用程序块和_Complete事件处理程序永远不会触发。从Windows窗体我可以通过使用“完整”标志和“while(!complete)application.doevents”来模拟同步运行的函数,但显然application.doevents在非Windows窗体应用程序中不可用。

是否有什么东西可以阻止我在Windows窗体应用程序之外使用类的方法(因为它继承自'Form')? 有什么方法可以解决这个问题吗?

谢谢, 麦克

4 个答案:

答案 0 :(得分:8)

尝试使用WaitHandle阻止当前线程而不是旋转并检查标志时,可能需要尝试类似下面的操作。

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();
    }
}

答案 1 :(得分:1)

我有更多关于这个问题的信息(我和mikecamimo在同一个团队工作)。

正确复制时,Windows窗体应用程序中也会出现此问题。在原始的OP中,Windows窗体中没有出现问题,因为没有阻塞。使用ResetEvent引入阻塞时,会出现同样的问题。

这是因为事件处理程序(Widget_Completed)与调用Widget.DoWork的方法位于同一个线程上。结果是AutoResetEvent.WaitOne();永远阻止因为永远不会调用事件处理程序来设置事件。

在Windows窗体环境中,可以通过使用Application.DoEvents轮询消息队列并允许处理事件来解决此问题。见下文。

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;
    }
}

在非Windows窗体应用程序(例如Windows服务)中,应用程序不可用,因此无法调用DoEvents。

问题是线程和widget之间的问题.DoWork的相关事件处理程序需要在另一个线程上。这应该可以防止AutoResetEvent.WaitOne无限期地阻塞。我想...... :)

关于如何实现这一目标的任何想法都会很棒。

答案 2 :(得分:1)

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();
}

答案 3 :(得分:0)

您是否拥有该组件的来源?听起来它依赖于它将从WinForms环境调用的事实(必须是一个很好的理由,为什么一个库继承自Form!),但很难确定。