如何取消阻止对 AutoResetEvent 对象调用 WaitOne 方法的线程?

发布于 2024-11-05 15:57:17 字数 1455 浏览 0 评论 0原文

下面是一个具有“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 文档。我引用以下文档的相关部分:

如果 millisecondsTimeout 为零,则该方法不会阻塞。它测试等待句柄的状态并立即返回。

在 do..while 循环中,我调用 Set方法。这会释放可能因调用 WaitOne 方法(在“SomeMethod”方法内编码)而阻塞的单个线程。接下来,我测试“theEvent”对象的状态,只是为了知道它是否已发出信号。此测试是通过调用带有超时参数的 WaitOne 方法的重载版本来完成的。我在调用 WaitOne 方法时使用的参数为零,根据文档,这会导致调用立即返回布尔值。如果返回值为 true,则“theEvent”对象处于有信号状态。如果在“SomeMethod”方法中调用“WaitOne”方法时至少有一个线程被阻塞,则对“Set”方法(在“UnblockAll”方法内编码)的调用将解除该线程的阻塞。因此,在“UnblockAll”方法中的 do..while 语句末尾调用“WaitOne”方法将返回 false。仅当没有线程被阻塞时,返回值才为 true。

上述推理是否正确?如果正确,该技术是否是处理我的问题的标准方法?我尝试主要在 .net Compact-framework 2.0 平台上使用该解决方案。

Below is a class having the method 'SomeMethod' that illustrates my problem.

class SomeClass
{
    AutoResetEvent theEvent = new AutoResetEvent(false);
    // more member declarations

    public void SomeMethod()
    {
        // some code
        theEvent.WaitOne();
        // more code
    }
}

The method is designed to be thread-safe and will be called in different threads. Now my question is how is it possible to unblock all threads that have called the 'WaitOne' method on the 'theEvent' object at any point of time? This requirement frequently arises in my design because I need to be able to gracefully stop and start my multi-threaded program. It seems to me that it's fairly simple to start a multi-threaded program, but tricky to stop one.

Here's what I have tried so far which apparently works. But is this the standard approach?

public void UnblockAll()
{
    do
    {
        theEvent.Set();
    } while (theEvent.WaitOne(0));
}

The 'UnblockAll' method is a member of the 'SomeClass' class. The technique used here is based on the MSDN documentation of the WaitOne method. I am quoting the relevant part of the documentation below:

If millisecondsTimeout is zero, the method does not block. It tests the state of the wait handle and returns immediately.

In the do..while loop, I call the Set method. This releases a single thread that may have blocked due to a call to the WaitOne method (coded inside the 'SomeMethod' method). Next I test the state of the 'theEvent' object just to know whether it's signalled. This test is done by calling the overloaded version of the WaitOne method that takes a time out parameter. The argument which I use when I call the WaitOne method is zero, which as per the documentation results in the call returning immediately with a boolean value. If the return value is true, then the 'theEvent' object was in a signalled state. If there was at least a single thread blocked on the call to the 'WaitOne' method in the 'SomeMethod' method, the call to the 'Set' method (coded inside the 'UnblockAll' method) would unblock it. Consequently the call to the 'WaitOne' method at the end of the do..while statement in the 'UnblockAll' method would return false. The return value is true only if there were no threads blocked.

Is the above reasoning right and if it's right, is the technique a standard way to deal with my problem? I am trying to use the solution primarily on the .net compact-framework 2.0 platform.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

情深已缘浅 2024-11-12 15:57:17

您有三个可行的选择。每一种都有其自身的优点和缺点。选择最适合您的具体情况的一种。

选项 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.Interrupt 将“戳”BCL 中使用的内置阻塞调用,包括 Thread.JoinWaitHandle.WaitOneThread。睡眠

You have three viable options. Each one has its own advantages and disadvantages. Pick the one that works best for your specific situation.

Option 1 - Poll the WaitHandle.

Instead of doing an indefinite blocking call use one with a timeout and reinstate the block if a shutdown request has not been given.

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.
}

Option 2 - Use a separate WaitHandle for requesting shutdown

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.
}

Option 3 - Use Thread.Interrupt

Do not confuse this with Thread.Abort. Aborting a thread is definitely unsafe, but interrupting a thread is completely different. Thread.Interrupt will "poke" the builtin blocking calls used in the BCL including Thread.Join, WaitHandle.WaitOne, Thread.Sleep, etc.

独孤求败 2024-11-12 15:57:17

您的例程可能在大多数情况下都有效,但我认为不能保证等待线程之一会在关闭循环设置事件的时间和关闭循环再次检查事件的时间之间重置事件。

我发现 AutoResetEvent 和 ManualResetEvent 类非常适合非常简单的场景。每当需求出现任何奇怪的情况时,我都会快速切换到更灵活的等待并脉冲模式

如果您不需要任何清理,您可以将工作线程设置为后台线程,然后当主线程退出时它们就会停止。

您还可以定义第二个名为 stopRequest 的 ManualResetEvent 并等待来自任一事件的信号。但是,紧凑框架可能不支持这一点。

Your routine will probably work most of the time, but I don't think there's any guarantee that one of the waiting threads will reset the event between the time that your shutdown loop sets it and the time that your shutdown loop checks it again.

I find that the AutoResetEvent and ManualResetEvent classes work great for really simple scenarios. Any time there's anything odd about the requirements, I quickly switch to the more flexible Wait And Pulse pattern.

If you don't require any clean up, you could make your worker threads background threads, and then they'll just stop when the main thread exits.

You might also be able to define a second ManualResetEvent called stopRequest and wait on a signal from either event. However, that might not be supported on the compact framework.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文