如何在等待事件的同时等待WinForms中的信号?

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

这行得通,但似乎浪费资源和黑客.

基本上,我认为我需要一种等待但又不阻塞线程的方法.这样做的正确方式是什么?

情况二

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

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对象,并且该方法需要在调用ApiSend()之前等待ApiNotifyHandler执行(即,发生ApiFound事件).

因此,这与案例1中的情况相同.我应该如何等待shouldContinue为真?

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

[更新]

我*使用.Net 2.0.

解决方法:

解决此问题的最佳方法是将代码重构为使用async / await,并将ApiStateUpdate事件转换为带有TaskCompletionSource(EAP pattern)的可等待任务.

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

[编辑]您想要在这里做的是异步到同步桥,这几乎总是一个坏主意.而且,我刚刚意识到您正在构造器中执行此操作.就其本质而言,构造函数内部不应包含任何异步代码,它们是原子的.总有一种更好的方法可以从构造函数中排除冗长的初始化过程. @StephenCleary在他非常有用的blog post中谈到了这一点.

关于.NET 2.0限制.尽管异步/等待可能是一个革命性的概念,但其背后的状态机概念并不是什么新鲜事物.您始终可以使用一系列委托回调和事件来模拟它.自.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(基于MsgWaitForMultipleObjects,从here开始)来实现异步到同步桥,如下所示:

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();
}
上一篇:JavaScript / jQuery仅在第二次单击时有效


下一篇:javascript-跨浏览器事件处理