c#线程学习笔记三---线程同步

线程的同步,通过WaitHandle类,及其继承EventWaitHandle类,及其以下的ManualResetEvent(手动重置)和AutoResetEvent(自动重置)事件来完成线程的同步功能

MSDN:WaitHandle:http://msdn.microsoft.com/zh-cn/library/fss7k4e9

          EventWaitHandle:http://msdn.microsoft.com/zh-cn/library/73zz66k2

          ManualResetEvent:http://msdn.microsoft.com/zh-cn/library/system.threading.manualresetevent

          AutoResetEvent:http://msdn.microsoft.com/zh-cn/library/system.threading.autoresetevent

 

ManualResetEvent是手动重置信号状态,AutoResetEvent是自动重置信号状态,当一开始利用

ManualResetEvent 事件对象 = new ManualResetEvent(false);
AutoResetEvent 事件对象=new AutoResetEvent(false);

将构造函数的参数设为false,即为无信号状态,在线程的执行中遇到

事件对象.WaitOne();

当前线程就会受到阻止,而停止运行,这时可以通过调用

事件对象.Set();

来激活事件对象的信号发送,从而WaitOne()方法收到信号后可以继续执行

 

但是,由于ManualResetEvent是手动重置信号状态,AutoResetEvent是自动重置信号状态,因此要恢复默认状态,ManualResetEvent对象必须调用

事件对象.Reset();

而AutoResetEvent对象,在本次事件执行完毕后即恢复无信号状态

 

代码例举:

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

namespace 线程同步3
{
    class Program
    {
        static ManualResetEvent mre = new ManualResetEvent(false);//声明手动ManualResetEvent对象

        static void Main(string[] args)
        {
            Thread mt = new Thread(myprocess);
            mt.Start();//开始执行线程
            
            for (int i = 0; i < 10; i++)
            {
                Console.ReadLine();//每按一次键盘,再将状态设为发送信号
                mre.Set();//设置为发信状态
            }
        }
        static void myprocess()
        {
            while (true)
            {
                Console.WriteLine("执行中");
                mre.WaitOne();//等待事件发信
                Console.WriteLine("执行结束");
                mre.Reset();//由于是手动事件,这里每次要将线程的状态重置为无信号
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace 线程同步4
{
    class Program
    {
        static AutoResetEvent are = new AutoResetEvent(false);
        static void Main(string[] args)
        {
            Thread mt = new Thread(myprocess);
            mt.Start();
            for (int i = 0; i < 10; i++)
            {
                Console.ReadLine();
                are.Set();
            }
        }
        static void myprocess()
        {
            while (true)
            {
                Console.WriteLine("执行中");
                are.WaitOne();
                Console.WriteLine("执行结束");//由于是自动事件,无需再手动调用Reset()来重置状态
            }
        }
    }
}                                                         

 

 

以下展示的是一个关于线程同步的实例,利用同步事件和信号状态来控制队列数据的添加和删除

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

namespace 线程同步2
{
    class Program
    {
        static void Main(string[] args)
        {
            SyncEvents syncEvents = new SyncEvents();
            Queue<int> queue = new Queue<int>();//创建一个先进先出的数据队列
            Console.WriteLine("配置工作线程...");
            Producer producer = new Producer(queue, syncEvents);//创建新增元素类对象,并以队列和同步事件对象初始化
            Consumer consumer = new Consumer(queue, syncEvents);//创建消耗元素类对象,并以相同的队列和同步事件对象初始化

            Thread pt = new Thread(producer.ThreadRun);//为对象的处理事件创建线程
            Thread ct = new Thread(consumer.ThreadRun);
            Console.WriteLine("启动线程....");
            pt.Start();//启动线程
            ct.Start();
            for (int i = 0; i < 4; i++)
            {
                Thread.Sleep(2500);//这里每隔2.5秒显示一次队列数列中的数据
                showQueueContents(queue);
            }
            Console.WriteLine("终止线程");
            syncEvents.ExitThreadEvent.Set();//为同步事件对象的退出(手动)事件,设置信号
            Console.WriteLine("主线程等待其余线程执行完毕");
            pt.Join();//用Join方法等待线程执行完毕
            ct.Join();
        }

        static void showQueueContents(Queue<int> q)
        {
            lock (((ICollection)q).SyncRoot)//对于数据的遍历,也要为其加上lock,以防止其他线程对其访问修改
            {
                foreach (int i in q)
                {
                    Console.Write("{0} ",  i);
                }
            }
            Console.WriteLine();
        }
    }

    class SyncEvents//同步事件类
    {
        public SyncEvents()//构造函数,初始化事件对象
        {
            _newItemEvent = new AutoResetEvent(false);//将事件对象均设置为无信号,并将新增设为自动重置事件、退出设置为手动重置事件
            _exitThreadEvent = new ManualResetEvent(false);
            _eventArray = new WaitHandle[2];
            _eventArray[0] = _newItemEvent;
            _eventArray[1] = _exitThreadEvent;
        }

        private EventWaitHandle _newItemEvent;//新建对象事件
        private EventWaitHandle _exitThreadEvent;//退出线程事件
        private WaitHandle[] _eventArray;//包含以上2个事件的事件队列

        public EventWaitHandle ExitThreadEvent
        {
            get { return _exitThreadEvent; }
        }
        public EventWaitHandle NewItemEvent
        {
            get { return _newItemEvent; }
        }
        public WaitHandle[] EventArray
        {
            get { return _eventArray; }
        }
    }
    class Producer
    {
        public Producer(Queue<int> q, SyncEvents e)//以事件对象和同步事件对象作为引用对象传入
        {
            _queue = q;
            _syncEvent = e;
        }
        private Queue<int> _queue;
        private SyncEvents _syncEvent;
        public void ThreadRun()
        {
            int count = 0;//执行次数计数器
            int r = 0;
            while (!_syncEvent.ExitThreadEvent.WaitOne(0, false))//当同步对象中的退出(手动)事件没有收到信号时继续执行
            {
                lock (((ICollection)_queue).SyncRoot)
                {
                    while (_queue.Count < 20)
                    {
                        _queue.Enqueue(r++);//向队列中添加元素
                        _syncEvent.NewItemEvent.Set();//通知触发同步对象中的新增(自动)事件来发送信号
                        count++;//执行次数计数器
                    }
                }
            }
            Console.WriteLine("制作线程:制造了{0}个",count);
        }
    }

    class Consumer
    {
        public Consumer(Queue<int> q, SyncEvents e)
        {
            _queue = q;
            _syncEvent = e;
        }
        private Queue<int> _queue;
        private SyncEvents _syncEvent;
        public void ThreadRun()
        {
            int count = 0;//计数器
            while (WaitHandle.WaitAny(_syncEvent.EventArray) != 1)//这里利用WaitHandle的WaitAny来判断事件队列哪个事件收到信号,只要不是退出(手动)事件收到信号,即可继续执行
            {
                lock (((ICollection)_queue).SyncRoot)
                {
                    _queue.Dequeue();//从队列中去除一个元素
                }
                count++;
            }
            Console.WriteLine("消费线程:消耗了{0}个",count);
        }
    }
}

 

由以上代码可见,本例利用一个集合了AutoResetEvent和ManualResetEvent的类型对象来控制线程同步,

 

在新增对象的ThreadRun方法中,利用ManualResetEvent的类型对象的WaitOne(0, false)方法来判读主线程是否给出退出信号,注意这里一用WaitOne方法的一个重载,其中设置第一个参数为所要等待事件发出信号时间

如果事件发信号超时,则直接继续执行,设为0则表示直接判断后无论是否收信直接继续执行

在消耗对象的ThreadRun方法中,利用同步对象中集合的AutoResetEvent和ManualResetEvent的一个事件队列,通过WaitHandle.WaitAny(WaitHandle[] waitHandles)方法来判断获取事件信号的对象是哪一个,并返回

队列索引值,这里在未收信时会一直处于等待状态,每当新增对象的ThreadRun方法新增对象后,会通过调用AutoResetEvent类型对象Set()方法给WaitHandle.WaitAny发信,从而使得消耗线程继续得以执行

从而,只要主线程不调用同步事件中退出事件的Set()来发信,那么每当新增线程执行,并调用新增事件的Set()方法时触发消耗线程的执行

最后执行结果为

 

 

 

                                            

posted @ 2012-06-17 15:58  MadKeX  阅读(446)  评论(0编辑  收藏  举报