WaitHandle的基本概念是什么?

70
C# .net中的WaitHandle基本概念是什么? 它的用途是什么? 何时使用? WaitHandle中的WaitAllWaitAny方法的用途是什么?
5个回答

63
每当您想要控制应用程序中多个线程的执行时。虽然这并不意味着只有一个线程会增加计数器;但让线程在事件发生时启动/停止或暂停。
请参阅WaitHandles - Auto/ManualResetEvent和Mutex --EDIT-- WaitHandle是您“使用”来控制线程执行的“机制”。它不是关于句柄在线程内不可访问;而是在线程内使用它们。
这可能是一个复杂的例子,请耐心看完;想象一下,一个女士给五个女孩五个不同的口哨,并告诉她们在something发生时吹哨子;过程是每个女孩都吹哨子,女士就会知道是谁吹了哨子。
现在,这不是关于彼此共享口哨,而是关于女士如何使用它们来“控制”女孩子们吹哨的过程。
因此,从技术上讲,处理过程如下:
1.创建等待事件(ManualResetEvent对象)
2.注册事件,WaitHandle.WaitAny(events); 3.在线程中完成操作后,.Set(),这将告诉WaitHandle:“我完成了!”
例如,考虑提供的链接中的示例。我已经为您添加了步骤以便您理解逻辑。这些不是硬编码的步骤,只是为了让您理解。
class Test
{
    static void Main()
    {
    //STEP 1: Create a wait handle
        ManualResetEvent[] events = new ManualResetEvent[10];//Create a wait handle
        for (int i=0; i < events.Length; i++)
        {
            events[i] = new ManualResetEvent(false);
            Runner r = new Runner(events[i], i); 
            new Thread(new ThreadStart(r.Run)).Start();
        }

    //STEP 2: Register for the events to wait for
        int index = WaitHandle.WaitAny(events); //wait here for any event and print following line.

        Console.WriteLine ("***** The winner is {0} *****", 
                           index);

        WaitHandle.WaitAll(events); //Wait for all of the threads to finish, that is, to call their cooresponding `.Set()` method.

        Console.WriteLine ("All finished!");
    }
}


class Runner
{
    static readonly object rngLock = new object();
    static Random rng = new Random();

    ManualResetEvent ev;
    int id;

    internal Runner (ManualResetEvent ev, int id)
    {
        this.ev = ev;//Wait handle associated to each object, thread in this case.
        this.id = id;
    }

    internal void Run()
    {
    //STEP 3: Do some work
        for (int i=0; i < 10; i++)
        {
            int sleepTime;
            // Not sure about the thread safety of Random...
            lock (rngLock)
            {
                sleepTime = rng.Next(2000);
            }
            Thread.Sleep(sleepTime);
            Console.WriteLine ("Runner {0} at stage {1}",
                               id, i);
        }

    //STEP 4: Im done!
        ev.Set();
    }
}

WaitAll() - 用于等待集合中所有句柄都被释放/发出信号... 这是否意味着除非它们被释放(其他线程应该在集合中等待这些句柄),否则句柄将不可访问给其他线程? - DotNetBeginner
1
@DotNetBeginner:这是否意味着Handles将无法被其他线程访问?请查看我的更新帖子,我添加了一个示例来回答这个问题。 - KMån
值得一提的是,ev.Set() 最好放在 finally 块中。 - Hakan Fıstık
为什么在谈论哨子时,一定要是女士“控制”女孩呢?也许是袋鼠被袋獾之类的动物赐予了哨子? - ruffin
3
因为必须要有个东西。袋鼠和沙袋鼠也不是更好的选择,特别是它们不会吹口哨,这在常识中并不被广泛认可。 - dynamichael

36

WaitHandle 是两个常用事件句柄类 AutoResetEventManualResetEvent 的抽象基类。

这两个类允许一个线程“通知”一个或多个其他线程。它们用于在线程之间同步(或串行化活动)。可以使用 SetWaitOne(或 WaitAll)方法来实现。例如:

线程1:

// do setup work

myWaitHandle.Set();

线程 2:

// do setup work

myWaitHandle.WaitOne();

// this code will not continue until after the call to `Set` 
// in thread 1 completes.

这只是一个非常基础的示例,网络上有许多类似的示例。基本思想是使用 WaitOne 等待来自另一个线程的信号,指示发生了某些事情。对于异步调用委托时返回的 AsyncWaitHandle (它会异步返回),WaitOne 允许您使当前线程等待直到异步操作完成。

AutoResetEventManualResetEvent 没有设置时,调用 WaitOne 将阻塞调用线程,直到调用 Set。这两个类唯一的区别在于,AutoResetEvent 在成功调用 WaitOne 完成后“取消”事件,使后续调用再次阻塞,直到调用 Set。必须通过调用 Reset 显式地取消 ManualResetEvent

WaitAllWaitAny 是位于 WaitHandle 类上的静态方法,允许您指定要等待的 WaitHandles 数组。 WaitAll 将阻塞,直到所有提供的句柄都已设置,而 WaitAny 仅在其中之一被设置时阻塞。


10

这是一个抽象类,不能直接使用。具体的派生类有ManualResetEvent、AutoResetEvent、Mutex和Semaphore。它们是实现线程同步的重要工具箱类。它们继承了WaitOne、WaitAll和WaitAny方法,您可以使用它们来检测一个或多个线程发出的等待条件信号。

Manual/AutoResetEvent的典型用法场景是告诉线程退出或让线程发出已经进入重要序列点的信号。Semaphore帮助您限制执行操作的线程数。或者实现不应与特定线程相关联的线程同步。Mutex则用于将代码段的所有权分配给一个线程,lock语句在那里也经常适用。

关于这方面的书籍已经写了很多。Joe Duffy的Concurrent Programming in Windows是最新、最好的一本。如果您考虑编写多线程代码,强烈推荐阅读。


8
想象一下,你有一个包含1000个项目的数组。你需要对每个项目进行一些处理。这项工作需要一些时间,但不受I/O限制。
例如,也许你需要使用每个项目来进行低带宽的Web请求。你有足够的吞吐量同时请求多个项目,并且每个Web请求中的延迟意味着逐个执行可能需要比你想要的时间更长。
进入并行编程的世界。今天有许多方法可以处理此任务,而WaitHandle是其中的基本部分。即使您不直接使用WaitHandle,您选择的任何选项都很可能依赖于WaitHandle,例如WaitAll或WaitAny在幕后起作用。
继续上面的例子,假设你有一个典型的四核CPU。在这种情况下,同时运行超过4个线程没有太多意义。*因此,有4个线程,但有1000个项目;你该怎么办?
一种选择使用WaitAny。你启动4个线程,每次WaitAny方法返回时,你就启动另一个线程,直到所有1000个项目都排队了。请注意,这不是WaitAny的好例子,因为我们知道总共有多少项目,并且可以以相同的速度访问任何项目。当您只有顺序访问时,WaitAny最好。还有其他类似的情况,WaitAny可能非常有意义。
另一种选择使用WaitAll。而不是逐个排队,您为每个四核心设置一个线程,并将其分配给数组的不同250个项目段。使用此选项,您可以使用WaitAll等待所有处理完成。

3
这里有一些非常长的答案。对于寻找简短答案的人:
等待句柄是一种机制,使一个线程等待另一个线程达到某个点。
您还可以有几个线程在等待和/或几个线程正在被等待,因此有WaitOneWaitAllWaitAny方法。通过选择这些类之一,可以选择可用的语义选项:MutexSemaphoreManualResetEventAutoResetEvent,这些都有很好的文档记录。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接