如何在WinForms中等待事件的同时收听事件?

问题描述 投票:3回答:3

案例1

这是我的设置。

internal class MyClass
{
    private ApiObject apiObject;

    private bool cond1;
    private bool cond2;

    internal MyClass()
    {
        this.apiObject = new ApiObject();
        this.apiObject.ApiStateUpdate += new ApiStateUpdateEventHandler(ApiStateHandler);

        //wait for both conditions to be true
    }

    private void ApiStateHandler(string who, int howMuch)
    {
        if(who.Equals("Something") && howMuch == 1)
            this.cond1 = true;
        else if(who.Equals("SomethingElse") && howMuch == 1)
            this.cond2 = true;
    }
}

我如何等待两个条件都成立

如果我这样做:

while(!(this.cond1 && this.cond2))
{
    System.Threading.Thread.Sleep(1000);
}

ApiStateHandler()中的代码似乎从未执行。

如果我这样做:

while(!(this.cond1 && this.cond2))
{
    System.Windows.Forms.Application.DoEvents();
}

这可行,但似乎浪费资源和黑客。

[基本上,我认为我需要一种方法来wait,但不要阻塞线程。这样做的适当方式是什么?

案例2

第二种情况有些相似(并相关)并说明了相同的问题。

internal class MyClass
{
    private ApiNotifyClass apiNotifyClass;
    private shouldContinue = false;

    internal MyClass()
    {
        //in addition to the code from above
        this.apiNotifyClass = new ApiNotifyClass();
        this.apiNotifyClass.ApiFound += ApiNofityFoundEventHandler(ApiNotifyHandler);
    }

    internal void Send(SomethingToSend somethigToSend)
    {
        Verifyer verifier = this.apiObject.ApiGet(somethingToSend);
        this.apiNotifyClass.ApiAttach(verifier);

        //wait for the shouldContinue to be true

        this.apiObject.ApiSend(verifier);

        this.apiNotifyClass.ApiDetach(verifier);
    }

    private void ApiNotifyHandler()
    {
        this.shouldContinue = true;
    }
}

[调用Send()时,将创建Verifier对象,并且该方法需要在调用ApiNotifyHandler之前等待ApiFound执行(即,发生ApiSend()事件。

因此,这与案例1中的情况相同。我应该如何[[等待shouldContinue成为现实?

很长的问题,很抱歉,但是我想提供尽可能多的信息来帮助您。

[

更新]

我被迫使用.Net 2.0。
c# multithreading winforms events handler
3个回答
2
投票
处理此问题的最佳方法是将代码重构为使用async/await,并使用ApiStateUpdateTaskCompletionSource)将EAP pattern事件转换为可等待的任务。

如果您真的要同步等待UI线程上的事件,请查看WaitWithDoEvents中的hereCoWaitForMultipleHandles中的here,他们就是这样做的。请记住,这种方法创建了一个嵌套的模态消息循环,可能的代码重入是最显着的含义(在细节here中进行了讨论)。

[[EDITED]在这里您想要做的是异步到同步的桥接,这几乎总是一个坏主意。而且,我刚刚意识到您正在构造器中执行此操作。就其本质而言,构造函数内部不应包含任何异步代码,它们是原子的。总有一种更好的方法可以从构造函数中排除冗长的初始化过程。 @StephenCleary在他的翔实blog post中谈到了这一点。关于.NET 2.0限制。尽管async/await可能是一个革命性的概念,但其背后的状态机概念并不是什么新鲜事物。您始终可以使用一系列委托回调和事件来模拟它。自.NET 2.0开始,匿名代表就在那里。例如,您的代码可能如下所示:

internal class MyClass { private ApiObject apiObject; public event EventHandler Initialized; internal MyClass() { this.apiObject = new ApiObject(); } public void Initialize() { ApiStateUpdateEventHandler handler = null; handler = delegate(string who, int howMuch) { bool cond1 = false; bool cond2 = false; if(who.Equals("Something") && howMuch == 1) cond1 = true; else if(who.Equals("SomethingElse") && howMuch == 1) cond2 = true; //wait for both conditions to be true if ( !cond1 && !cond2 ) return; this.apiObject.ApiStateUpdate -= handler; // fire an event when both conditions are met if (this.Initialized != null) this.Initialized(this, new EventArgs()); }; this.apiObject.ApiStateUpdate += handler; } }

使用MyClass的客户端代码可能看起来像这样:

MyClass myObject = new MyClass(); myObject.Initialized += delegate { MessageBox.Show("Hello!"); }; myObject.Initialize();

以上将是用于.NET 2.0的正确的基于异步事件的模式

。一个更简单但更糟糕的解决方案是使用WaitWithDoEvents(基于here,从MsgWaitForMultipleObjects开始)实现异步到同步桥,如下所示:internal class MyClass { private ApiObject apiObject; internal MyClass() { this.apiObject = new ApiObject(); Initialize(); } private void Initialize() { using (ManualResetEvent syncEvent = new ManualResetEvent()) { ApiStateUpdateEventHandler handler = null; handler = delegate(string who, int howMuch) { bool cond1 = false; bool cond2 = false; if(who.Equals("Something") && howMuch == 1) cond1 = true; else if(who.Equals("SomethingElse") && howMuch == 1) cond2 = true; //wait for both conditions to be true if ( !cond1 && !cond2 ) return; this.apiObject.ApiStateUpdate -= handler; syncEvent.Set(); }; this.apiObject.ApiStateUpdate += handler; WaitWithDoEvents(syncEvent, Timeout.Infinite); } } }
但比您的问题中的繁忙等待循环还要有效:

while(!(this.cond1 && this.cond2)) { System.Windows.Forms.Application.DoEvents(); }


1
投票
您将不得不异步执行阻止代码。否则,您将挂断不好的UI线程。有多种不同的方法可以做到这一点。这是使用新的asyncawait关键字的一种。我承认,这很容易吞咽,并且仅在.NET 4.5+中才可行。

0
投票
[基本上,我认为我需要一种等待方式,但又不能阻止线程。
© www.soinside.com 2019 - 2024. All rights reserved.