下面是一个具有"SomeMethod"方法的类,它说明了我的问题。
class SomeClass
{
AutoResetEvent theEvent = new AutoResetEvent(false);
// more member declarations
public void SomeMethod()
{
// some code
theEvent.WaitOne();
// more code
}
}
该方法设计为线程安全,并将在不同的线程中调用。现在我的问题是,如何在任何时间点取消阻止在"theEvent"对象上调用"WaitOne"方法的所有线程?这个要求在我的设计中经常出现,因为我需要能够优雅地停止和启动我的多线程程序。在我看来,启动一个多线程程序相当简单,但停止一个程序却很棘手。
这是我到目前为止尝试过的显然有效的方法。但这是标准方法吗?
public void UnblockAll()
{
do
{
theEvent.Set();
} while (theEvent.WaitOne(0));
}
"UnblockAll"方法是"SomeClass"类的成员。此处使用的技术基于 WaitOne 方法的 MSDN 文档。我引用以下文档的相关部分:
如果毫秒超时为零,则该方法不会阻止。它测试等待句柄的状态并立即返回。
在做..在循环中,我调用 Set 方法。这将释放一个线程,该线程可能由于调用 WaitOne 方法(在"SomeMethod"方法中编码)而阻塞。接下来,我测试"theEvent"对象的状态,只是为了知道它是否发出了信号。此测试是通过调用采用超时参数的 WaitOne 方法的重载版本来完成的。调用 WaitOne 方法时使用的参数为零,根据文档,这会导致调用立即返回布尔值。如果返回值为 true,则"theEvent"对象处于信号状态。如果在调用"SomeMethod"方法中的"WaitOne"方法时至少有一个线程被阻塞,则对"Set"方法(在"UnblockAll"方法中编码)的调用将取消阻止它。因此,在 do 结束时调用"WaitOne"方法。而"UnblockAll"方法中的语句将返回 false。仅当没有阻塞线程时,返回值才为 true。
上述推理是否正确,如果正确,该技术是处理我问题的标准方法吗?我正在尝试主要在.net紧凑框架2.0平台上使用该解决方案。
您有三个可行的选择。每个都有自己的优点和缺点。选择最适合您具体情况的一种。
选项 1 - 轮询WaitHandle
。
与其执行无限期阻塞调用,请使用超时调用,如果未给出关闭请求,则恢复阻止。
public void SomeMethod()
{
while (!yourEvent.WaitOne(POLLING_INTERVAL))
{
if (IsShutdownRequested())
{
// Add code to end gracefully here.
}
}
// Your event was signaled so now we can proceed.
}
选项 2 - 使用单独的WaitHandle
请求关闭
public void SomeMethod()
{
WaitHandle[] handles = new WaitHandle[] { yourEvent, shutdownEvent };
if (WaitHandle.WaitAny(handles) == 1)
{
// Add code to end gracefully here.
}
// Your event was signaled so now we can proceed.
}
选项 3 - 使用Thread.Interrupt
不要将其与Thread.Abort
混淆。中止线程肯定是不安全的,但中断线程则完全不同。 Thread.Interrupt
将"戳"BCL中使用的内置阻塞调用,包括Thread.Join
,WaitHandle.WaitOne
,Thread.Sleep
等。
您的例程可能会在大部分时间工作,但我认为无法保证其中一个等待线程会在关闭循环设置它的时间和关闭循环再次检查它的时间之间重置事件。
我发现自动重置事件和手动重置事件类非常适合非常简单的方案。每当需求有任何奇怪之处时,我都会迅速切换到更灵活的等待和脉冲模式。
如果不需要任何清理,则可以使工作线程成为后台线程,然后在主线程退出时停止。
您还可以定义第二个名为 stopRequest 的手动重置事件,并等待来自任一事件的信号。但是,紧凑的框架可能不支持这一点。
线程中止是否适用于您的框架?