网络世界,大有可为

献给自己的一片天空

导航

(转)对Queue的同步操作,了解lock以及 AutoResetEvent 和 ManualResetEvent 类用法

代码如下:

  1using System;
  2using System.Collections.Generic;
  3using System.Text;
  4
  5using System.Threading;
  6using System.Collections;
  7
  8
  9namespace SyncEvents
 10{
 11    public class SyncEvents
 12    {
 13
 14        public SyncEvents()
 15        {
 16            //AutoResetEvent 类用于“新项”事件,因为您希望每当使用者线程响应此事件后,此事件都能自动重置
 17            _newItemEvent = new AutoResetEvent(false);
 18           
 19            //ManualResetEvent 类用于“退出”事件,因为您希望当此事件终止时有多个线程响应
 20            _exitThreadEvent = new ManualResetEvent(false);
 21
 22            //此数组是必需的,因为它使使用者线程可以响应两个事件中的任何一个。
 23            _eventArray = new WaitHandle[2];
 24
 25            _eventArray[0= _newItemEvent;
 26
 27            _eventArray[1= _exitThreadEvent;
 28
 29        }

 30
 31        public EventWaitHandle ExitThreadEvent
 32        {
 33
 34            get return _exitThreadEvent; }
 35
 36        }

 37
 38        public EventWaitHandle NewItemEvent
 39        {
 40
 41            get return _newItemEvent; }
 42
 43        }

 44
 45        public WaitHandle[] EventArray
 46        {
 47
 48            get return _eventArray; }
 49
 50        }

 51        //制造者线程用来在有新项添加到队列中时通知使用者线程
 52        private EventWaitHandle _newItemEvent;
 53        //用来通知辅助线程终止
 54        private EventWaitHandle _exitThreadEvent;
 55        //这两个事件对象封装在一个名为 SyncEvents 的类中。这使事件可以轻松传递到表示制造者线程和使用者线程的对象。
 56        private WaitHandle[] _eventArray;
 57
 58    }

 59    /// <summary>
 60    /// 制造者线程
 61    /// </summary>

 62    public class Producer
 63    {
 64
 65        public Producer(Queue<int> q, SyncEvents e)
 66        {
 67
 68            _queue = q;
 69
 70            _syncEvents = e;
 71
 72        }

 73
 74        public void ThreadRun()
 75        {
 76
 77            int count = 0;
 78
 79            Random r = new Random();
 80            //WaitOne 使用的第一个参数为零,这表示该方法应立即返回。
 81            while (!_syncEvents.ExitThreadEvent.WaitOne(0false))
 82            {
 83                //在添加新项前,该集合必须处于锁定状态,以防止使用者线程和主线程同时访问该集合。ICollection 接口公开的 SyncRoot 字段。此字段专门为同步线程访问而提供
 84                lock (((ICollection)_queue).SyncRoot)
 85                {
 86
 87                    while (_queue.Count < 20)
 88                    {
 89
 90                        _queue.Enqueue(r.Next(0100));
 91
 92                        //对于制造者添加到队列中的每个新项,都将调用“新项”事件的 Set 方法。这将通知使用者线程离开挂起状态并开始处理新项。
 93                        _syncEvents.NewItemEvent.Set();
 94
 95                        count++;
 96                    }

 97                }

 98            }

 99
100            Console.WriteLine("Producer thread: produced {0} items", count);
101        }

102
103        private Queue<int> _queue;
104
105        private SyncEvents _syncEvents;
106
107    }

108    /// <summary>
109    /// 使用者线程
110    /// </summary>

111    public class Consumer
112    {
113
114        public Consumer(Queue<int> q, SyncEvents e)
115        {
116
117            _queue = q;
118
119            _syncEvents = e;
120
121        }

122
123        public void ThreadRun()
124        {
125
126            int count = 0;
127            //使用 WaitAny 来阻止使用者线程,直到所提供的数组中的任意一个等待句柄变为终止状态
128            //此数组是必需的,因为它使使用者线程可以响应两个事件中的任何一个。数组中有两个句柄,一个用来终止辅助线程,另一个用来指示有新项添加到集合中
129            while (WaitHandle.WaitAny(_syncEvents.EventArray) != 1)
130            {
131
132                lock (((ICollection)_queue).SyncRoot)
133                {
134
135                    int item = _queue.Dequeue();
136
137                }

138
139                count++;
140
141            }

142
143            Console.WriteLine("Consumer Thread: consumed {0} items", count);
144
145        }

146
147        private Queue<int> _queue;
148
149        private SyncEvents _syncEvents;
150
151    }

152    /// <summary>
153    /// 该示例创建两个辅助线程。一个线程生成元素并将它们存储在非线程安全的泛型队列中。有关更多信息,请参见 Queue。另一个线程使用此队列中的项。另外,主线程定期显示队列的内容,因此该队列被三个线程访问。lock 关键字用于同步对队列的访问,以确保队列的状态没有被破坏。
154    /// </summary>

155    public class ThreadSyncSample
156    {
157       private static void ShowQueueContents(Queue<int> q)
158        {
159            //ShowQueueContents 是由主线程执行的,因为它被 Main 调用。这意味着当此方法获得对项队列的独占访问权限时,它实际上既阻止了制造者线程访问队列,也阻止了使用者线程访问队列。
160            lock (((ICollection)q).SyncRoot)
161            {
162
163                foreach (int item in q)
164                {
165
166                    Console.Write("{0} ", item);
167
168                }

169            }

170
171            Console.WriteLine();
172
173        }

174
175        static void Main()
176        {
177
178            Queue<int> queue = new Queue<int>();
179
180            SyncEvents syncEvents = new SyncEvents();
181
182            Console.WriteLine("Configuring worker threads");
183
184            Producer producer = new Producer(queue, syncEvents);
185
186            Consumer consumer = new Consumer(queue, syncEvents);
187
188            Thread producerThread = new Thread(producer.ThreadRun);
189
190            Thread consumerThread = new Thread(consumer.ThreadRun);
191
192            Console.WriteLine("Launching producer and consumer threads");
193            //创建了两个新的辅助线程,它们独立于当前正在执行 Main 方法的主线程开始异步执行过程
194            producerThread.Start();
195
196            consumerThread.Start();
197            //Main 接下来要做的事情是通过调用 Sleep 方法将主线程挂起。该方法将当前正在执行的线程挂起指定的时间(毫秒)。在此时间间隔过后,Main 将重新激活,这时它将显示队列的内容。Main 重复此过程四次
198            for (int i = 0; i < 4; i++)
199            {
200
201                Thread.Sleep(2500);
202
203                ShowQueueContents(queue);
204
205            }

206
207            Console.WriteLine("Signaling threads to terminate");
208            //Main 通过调用“退出线程”事件的 Set 方法通知辅助线程终止,然后对每个辅助线程调用 Join 方法以阻止主线程,直到每个辅助线程都响应该事件并终止。
209            syncEvents.ExitThreadEvent.Set();
210
211            producerThread.Join();
212
213            consumerThread.Join();
214
215        }

216    }

217}

218

posted on 2007-07-27 09:59  Geerry  阅读(854)  评论(0编辑  收藏  举报