案例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。async/await
,并使用ApiStateUpdate
(TaskCompletionSource
)将EAP pattern事件转换为可等待的任务。如果您真的要同步等待UI线程上的事件,请查看WaitWithDoEvents
中的here或CoWaitForMultipleHandles
中的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();
}
async
和await
关键字的一种。我承认,这很容易吞咽,并且仅在.NET 4.5+中才可行。[基本上,我认为我需要一种等待方式,但又不能阻止线程。