【问题标题】:What is the basic concept behind WaitHandle?WaitHandle 背后的基本概念是什么?
【发布时间】:2010-03-29 13:10:47
【问题描述】:

C# .net 线程中WaitHandle 背后的基本概念是什么? 它有什么用? 什么时候使用它? 里面的WaitAllWaitAny方法有什么用?

【问题讨论】:

    标签: c# .net multithreading


    【解决方案1】:

    当您想要控制应用中多个线程的执行时。虽然这不仅意味着只有一个线程递增计数器;但让线程启动/停止或暂停事件。

    WaitHandles - Auto/ManualResetEvent and Mutex

    --编辑--

    WaitHandles 是您“使用”来控制线程执行的机制。它不是关于无法在线程中访问的句柄;它关于在线程中使用它们。

    这可能是一个胖例子,但请多多包涵;想一想,一位女士给了五个女孩五种不同的口哨,并告诉她们只要有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: Does it mean that Handles will not be accessible to other threads;请参阅我更新的帖子,添加一个示例来回答这个问题。
    • 值得一提的是,ev.Set() 最好放在finally 块中
    • 当我们在吹口哨的时候,为什么一定要一位女士“控制”女孩?也许是小袋鼠给袋鼠吹口哨之类的?
    • ruffin:因为它必须是某种东西。袋鼠和小袋鼠也好不到哪里去,尤其是因为它们通常不会吹口哨。
    【解决方案2】:

    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 完成后“取消设置”事件,使后续调用再次阻塞,直到调用SetManualResetEvent 必须通过调用 Reset 显式“取消设置”。

    WaitAllWaitAnyWaitHandle 类上的静态方法,允许您指定要等待的 WaitHandles 数组。 WaitAll 将阻塞直到所有提供的句柄都是Set,而WaitAny 只会阻塞直到一个得到Set

    【讨论】:

      【解决方案3】:

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

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

      关于它的书籍已经写了。 Joe Duffy 的Concurrent Programming in Windows 是最新最好的。如果您打算编写线程代码,强烈推荐。

      【讨论】:

        【解决方案4】:

        假设您有一个包含 1000 个项目的数组。您需要对这些项目中的每一项进行一些处理。这项工作需要一些时间,但不受 I/O 限制。

        例如,您可能需要使用每个项目来发出低带宽网络请求。您有足够的吞吐量来同时请求许多项目,并且每个 Web 请求的延迟意味着一次执行一个请求可能需要比您想要的更长的时间。

        进入并行编程的世界。今天有很多方法可以处理这个任务,WaitHandle 是其中的一个基本部分。即使您不直接使用WaitHandle,您选择的任何选项都可能在幕后依赖WaitHandle,例如WaitAllWaitAny

        继续这个例子,假设您有一个典型的四核 CPU。在这种情况下,同时运行 4 个以上的线程没有多大意义。* 所以 4 个线程,但是 1000 个项目;你做什么工作?

        一个选项使用WaitAny。您启动了 4 个线程,并且每次 WaitAny 方法返回您启动另一个,直到所有 1000 个项目都排队。请注意,对于WaitAny,这是一个糟糕的示例,因为我们知道总共有多少项目,并且可以以相同的速率访问任何项目。当您只有顺序访问时,WaitAny 是最好的。还有其他类似的情况,WaitAny 可能很有意义。

        另一个选项使用WaitAll。您不是一次将一个项目排队,而是为四个核心中的每一个设置一个线程,为其分配一个不同的 250 项数组段。使用此选项,您可以使用WaitAll 等待所有处理完成。


        * 实际上,确实如此。通常有一定数量的 I/O 时间 CPU 会处于空闲状态,这样您可以通过让每个内核运行多个线程来做得更好。但那是另一个故事了。

        【讨论】:

          【解决方案5】:

          这里有一些很长的答案。对于任何寻找简短答案的人:

          等待句柄是一种使一个线程等待直到另一个线程到达某个点的机制。

          您还可以有多个线程等待和/或多个线程正在等待,因此WaitOneWaitAllWaitAny 方法。通过选择以下类之一,还有几个可用的语义选项:MutexSemaphoreManualResetEventAutoResetEvent,这些都有很好的记录。

          【讨论】:

            猜你喜欢
            • 2012-04-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-01-15
            • 2022-01-10
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多