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

发布于 2024-07-12 02:37:40 字数 423 浏览 7 评论 0原文

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

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

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

谢谢, 麦克风

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

不知在何时 2024-07-19 02:37:40

尝试一下可能值得尝试类似下面的方法,它使用 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();
    }
}

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();
    }
}
梦毁影碎の 2024-07-19 02:37:40

我有关于这个问题的更多信息(我和 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 无限期阻塞。 我认为...:)

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

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.

感受沵的脚步 2024-07-19 02:37:40
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();
}
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();
}
傲鸠 2024-07-19 02:37:40

您有该组件的来源吗? 听起来它依赖于从 WinForms 环境调用它的事实(这一定是库从 Form 继承的一个很好的理由!),但很难确定。

Do you have the source for the component? It sounds like it's relying on the fact it will be called from a WinForms environment (must be a good reason why a library inherits from Form!), but it's hard to know for sure.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文