有什么不明白的地方,扫描右方二维码加我微信交流。
       

ManualResetEvent类是一个比较常用的线程间通信工具,可以手动阻塞线程,所以在线程同步方面非常有用。

主要使用3个方法:

  • WaitOne
  • Set
  • Reset

先引入一个概念,信号。因为官方解释中使用的是signaled,所以在这里翻译为信号。那么信号是什么,接着往下看。

WaitOne

调用WaitOne时会导致线程阻塞,但此时是否阻塞是有条件的,要看此时是否有信号。有信号就不阻塞,无信号就阻塞。那么是否有信号怎么判断?不用判断,这个我们不用关心,我们需要做的是控制什么时候有信号,什么时候无信号。

Set

调用Set方法,此时为有信号状态。

Reset

调用Reset方法,此时为无信号状态。

拿生产者和消费者都举例:

生产者线程:我又造出10个产品,快来消费吧!(调用Set接口,切换为有信号状态,通知消费者线程可以开始消费了)。

消费者线程:收到通知,消费这10个产品,消费完成后,又处于等待状态。(调用Reset接口,切换为无信号状态,再调用WaitOne接口,阻塞消费线程)

下面上代码:

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

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {
            new ProductAndCostTester();
        }
    }

    /// <summary>
    /// 生产消费模型
    /// </summary>
    public class ProductAndCostTester
    {
        /// <summary>
        /// 生产线1线程
        /// </summary>
        private Thread _producterThread1;
        /// <summary>
        /// 生产线2线程
        /// </summary>
        private Thread _producterThread2;
        /// <summary>
        /// 消费线线程
        /// </summary>
        private Thread _costerThread;
        /// <summary>
        /// 产品列表
        /// </summary>
        private List<int> _goodList;
        /// <summary>
        /// ManualResetEvent实例
        /// </summary>
        private ManualResetEvent _mre;

        public ProductAndCostTester()
        {
            _goodList = new List<int>();

            _mre = new ManualResetEvent(false);//false初始化状态为无信号,将使WaitOne阻塞

            _producterThread1 = new Thread(Product1);
            _producterThread1.Name = "Productor1";
            _producterThread1.Start();

            _producterThread2 = new Thread(Product2);
            _producterThread2.Name = "Productor2";
            _producterThread2.Start();

            _costerThread = new Thread(Cost);
            _costerThread.Name = "Costor";
            _costerThread.Start();
        }

        /// <summary>
        /// 生产线1
        /// </summary>
        void Product1()
        {
            while (true)
            {
                Console.WriteLine(Thread.CurrentThread.Name + ":" + DateTime.Now.ToString("HH:mm:ss"));
                for (int i = 0; i < 3; i++)
                {
                    _goodList.Add(1);
                }
                _mre.Set();//表示有信号了,通知WaitOne不再阻塞

                Thread.Sleep(8000);
            }
        }

        /// <summary>
        /// 生产线2
        /// </summary>
        void Product2()
        {
            while (true)
            {
                Console.WriteLine(Thread.CurrentThread.Name + ":" + DateTime.Now.ToString("HH:mm:ss"));
                for (int i = 0; i < 6; i++)
                {
                    _goodList.Add(1);
                }
                _mre.Set();//表示有信号了,通知WaitOne不再阻塞

                Thread.Sleep(10000);
            }
        }

        /// <summary>
        /// 消费线
        /// </summary>
        void Cost()
        {
            while (true)
            {
                if (_goodList.Count > 0)
                {
                    Console.WriteLine("Cost " + _goodList.Count + " at " + DateTime.Now.ToString("HH:mm:ss"));
                    _goodList.Clear();
                    _mre.Reset();//重置为无信号了,使WaitOne可以再次阻塞
                }
                else
                {
                    Console.WriteLine("No cost at " + DateTime.Now.ToString("HH:mm:ss"));
                    _mre.WaitOne();//如果没有可消费的产品,即无信号,则会阻塞
                }
            }
        }
    }
}

以上代码可以直接拷贝到脚本中执行,亲测。

发表评论

电子邮件地址不会被公开。 必填项已用*标注