以下是一个例子:
if (control.InvokeRequired)
{
control.BeginInvoke(action, control);
}
else
{
action(control);
}
如果在条件和BeginInvoke调用之间调用控件,例如?
,该怎么办?另一个与事件有关的例子:
var handler = MyEvent;
if (handler != null)
{
handler.BeginInvoke(null, EventArgs.Empty, null, null);
}
如果在第一行和if语句之间取消订阅MyEvent
,则仍将执行if语句。但是,这是正确的设计吗?如果使用取消订阅也会破坏正确调用事件所需的状态?这个解决方案不仅具有更多代码行(样板文件),而且它不那么直观,并且可能导致客户端出现意外结果。
你说什么,是吗?
答案 0 :(得分:5)
在我看来,如果这是一个问题,你的线程管理和对象生命周期管理都是鲁莽的,需要重新审视。
在第一个示例中,代码不对称:BeginInvoke
不会等待action
完成,但直接调用会;这可能已经是一个错误了。
如果你期望另一个线程可能处理你正在使用的控件,你别无选择,只能抓住ObjectDisposedException
- 并且在你已经在action
之前可能不会抛出它。 1}},并且由于BeginInvoke
而可能不在当前线程上。
假设一旦您取消订阅活动,您将不再收到有关通知的通知,这是不恰当的。它甚至不需要多个线程来实现 - 只有多个订户。如果第一订户在处理导致第二订户取消订阅的通知时做某事,则当前“在飞行中”的通知仍将转到第二订户。您可以使用事件处理程序例程顶部的guard子句来缓解此问题,但您无法阻止它发生。
答案 1 :(得分:3)
有一些解决竞争条件的技巧:
哪个选项“正确”取决于应用程序。每个选项都有性能权衡,这可能会使并发性的好处不那么吸引人。
答案 2 :(得分:-1)
如果此行为在应用程序中传播多个位置,则可能需要重新设计API,如下所示:
if(!control.invokeIfRequired()){
action(action);
}
与标准JDK库ConcurrentHashMap.putIfAbsent(...)
的想法相同。当然,您需要在这个新的control.invokeIfRequired()
方法中处理同步。