zoukankan      html  css  js  c++  java
  • 多线程的一个问题(同步事件和等待句柄)转

          同步事件和等待句柄用于解决更复杂的同步情况,比如一个一个大的计算步骤包含3个步骤result = first term + second term + third term,如果现在想写个多线程程序,同时计算first term,second term 和third term,等所有3个步骤计算好后再把它们汇总起来,我们就需要使用到同步事件和等待句柄,同步事件分有两个,分别为AutoResetEvent和ManualResetEvent,这两个类可以用来代表某个线程的运行状态:终止和非终止,等待句柄用来判断ResetEvent的状态,如果是非终止状态就一直等待,否则放行,让等待句柄下面的代码继续运行。下面的代码示例阐释了如何使用等待句柄来发送复杂数字计算的不同阶段的完成信号。此计算的格式为:result = first term + second term + third term ;

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;

    namespace PrintThreadSample
    {
        class CalculateTest
        {   
            static void Main()   
            {       
                Calculate calc = new Calculate();       
                Console.WriteLine("Result = {0}.",calc.Result(234).ToString());       
                Console.WriteLine("Result = {0}.",calc.Result(55).ToString());

                Console.Read();
            }
        }
        class Calculate
        {   
            double baseNumber, firstTerm, secondTerm, thirdTerm;   
            AutoResetEvent[] autoEvents;   
            ManualResetEvent manualEvent;
            Random randomGenerator;     // Generate random numbers to simulate the actual calculations.  
            public Calculate()   
            {       
                autoEvents = new AutoResetEvent[]
                {           
                    new AutoResetEvent(false),           
                    new AutoResetEvent(false),           
                    new AutoResetEvent(false)       
                };       
                manualEvent = new ManualResetEvent(false);   
            }   
            void CalculateBase(object stateInfo)   
            {       
                baseNumber = randomGenerator.NextDouble();
                Console.WriteLine("baseNumber: {0}", baseNumber);
                manualEvent.Set();      // Signal that baseNumber is ready.    

            }   
            // The following CalculateX methods all perform the same   
            // series of steps as commented in CalculateFirstTerm.   
            void CalculateFirstTerm(object stateInfo)   
            {
                double preCalc = randomGenerator.NextDouble();// Perform a precalculation.
                manualEvent.WaitOne();       // Wait for baseNumber to be calculated.
                firstTerm = preCalc * baseNumber * randomGenerator.NextDouble();// Calculate the first term from preCalc and baseNumber.
                Console.WriteLine("FirstTerm: {0}", firstTerm);
                autoEvents[0].Set();      // Signal that the calculation is finished.
            }   
            void CalculateSecondTerm(object stateInfo)   
            {       
                double preCalc = randomGenerator.NextDouble();       
                manualEvent.WaitOne();       
                secondTerm = preCalc * baseNumber * randomGenerator.NextDouble();
                Console.WriteLine("SecondTerm: {0}", secondTerm);
                autoEvents[1].Set();   
            }   
            void CalculateThirdTerm(object stateInfo)   
            {       
                double preCalc = randomGenerator.NextDouble();       
                manualEvent.WaitOne();       
                thirdTerm = preCalc * baseNumber * randomGenerator.NextDouble();
                Console.WriteLine("ThirdTerm: {0}", thirdTerm);
                autoEvents[2].Set();   
            }   
            public double Result(int seed)   
            {       
                randomGenerator = new Random(seed);        // Simultaneously calculate the terms.       
                ThreadPool.QueueUserWorkItem(new WaitCallback(CalculateBase));       
                ThreadPool.QueueUserWorkItem(new WaitCallback(CalculateFirstTerm));       
                ThreadPool.QueueUserWorkItem(new WaitCallback(CalculateSecondTerm));       
                ThreadPool.QueueUserWorkItem(new WaitCallback(CalculateThirdTerm));
                WaitHandle.WaitAll(autoEvents); // Wait for all of the terms to be calculated.  
                manualEvent.Reset();            // Reset the wait handle for the next calculation.  
                return firstTerm + secondTerm + thirdTerm;   
            }
        }

    }

      该示例一共有4个ResetEvent,一个ManualEvent,三个AutoResetEvent,分别反映4个线程的运行状态。ManualEvent和AutoResetEvent有一点不同:AutoResetEvent是在当前线程调用set方法激活某线程之后,AutoResetEvent状态自动重置,而ManualEvent则需要手动调用Reset方法来重置状态。接着来看看上面那段代码的执行顺序,Main方法首先调用的是Result 方法,Result方法开启4个线程分别去执行,主线程阻塞在WaitHandle.WaitAll(autoEvents)处,等待3个计算步骤的完成。4个ResetEvent初始化状态都是非终止(构造实例时传入了false),CalculateBase首先执行完毕,其他3个线程阻塞在manualEvent.WaitOne()处,等待CalculateBase执行完成。CalculateBase生成baseNumber后,把代表自己的ManualEvent状态设置为终止状态。其他几个线程从manualEvent.WaitOne()处恢复执行,在执行完自己的代码后把自己对应的AutoResetEvent状态置为终止。当3个计算步骤执行完后,主线程从阻塞中恢复,把三个计算结果累加后返回。还要多补充一点的是WaitHandle的WaitOne,WaitAll,WaitAny方法,如果等待多个进程就用WaitAll,如本例中的:WaitHandle.WaitAll(autoEvents),WaitAny是等待的线程中有一个结束则停止等待。

  • 相关阅读:
    程序笔记
    2011年11月28日学习重构
    经典到发狂的语录(某男日记摘录)
    每天养成好习惯
    jquery用法
    [读书笔记]软件架构师应该知道的97件事
    Entity Framework 4.2发布,部分更新等待.NET Framework 4.5
    我的阅书记录及相关专业书籍推荐(更新于2017.12)
    [2011 年终项目总结] 第四章、架构设计
    [2011 年终项目总结] 第五章、迭代开发
  • 原文地址:https://www.cnblogs.com/MayGarden/p/1760923.html
Copyright © 2011-2022 走看看