c#多线程同步之EventWaitHandle再次使用
1 /// <summary> 2 /// 文件传输器,用来获取全文文件,自动根据全文文件数量,开启一定数量的线程,采用生产者消费模式 3 /// </summary> 4 public class FileTranser 5 { 6 private static IFileTranser Transer = new RealFileTranser(); 7 // 文件队列 8 static Queue<FullTextListViewModel> FileTaskQueue = new Queue<FullTextListViewModel>(); 9 // 为保证线程安全,使用一个锁来保护队列的访问 10 readonly static object locker = new object(); 11 // 通过 autoResetEvent 给工作线程发信号 12 static EventWaitHandle autoResetEvent = new AutoResetEvent(false); 13 public static CancellationTokenSource cancel = null; 14 15 static int MaxThreadCount = 5; 16 static int MinThreadCount = 1; 17 static int PageSize = 100; 18 19 static List<Thread> threads = new List<Thread>(); 20 static bool IsRunning = false; 21 public static void Start(FullTextListViewModel model, int total) 22 { 23 if (cancel != null && cancel.IsCancellationRequested) return; 24 if (IsRunning == false || threadCount > threads.Count) 25 { 26 int startCount = threadCount - threads.Count; 27 IsRunning = true; 28 // 任务开始,启动工作线程 29 for (int i = 0; i < startCount; i++) 30 { 31 Thread t = new Thread(Work); 32 t.Name = "f_" + (i + 1); 33 t.IsBackground = false; 34 t.Start(); 35 threads.Add(t); 36 } 37 } 38 lock (locker) 39 { 40 FileTaskQueue.Enqueue(model); 41 } 42 autoResetEvent.Set(); 43 } 44 public static void Cancel() 45 { 46 IsRunning = false; 47 threads.Clear(); 48 if (cancel != null) 49 { 50 cancel.Cancel(); 51 } 52 autoResetEvent.Set(); 53 Logger.Log("取消获取全文的线程执行"); 54 } 55 /// <summary>执行工作</summary> 56 static void Work() 57 { 58 while (true) 59 { 60 if (cancel.IsCancellationRequested) 61 { 62 Logger.Log("线程已取消,当前线程:" + Thread.CurrentThread.Name); 63 autoResetEvent.Set(); 64 break; 65 } 66 else 67 { 68 FullTextListViewModel model = null; 69 lock (locker) 70 { 71 if (FileTaskQueue.Count > 0) 72 { 73 model = FileTaskQueue.Dequeue(); 74 } 75 } 76 if (model != null) 77 { 78 Logger.Log("全文传输文件已经开始,当前Id:" + model.ClientTaskId + ",当前线程:" + Thread.CurrentThread.Name); 79 FileTranser.Transer.DoWork(model); 80 } 81 else 82 { 83 Logger.Log("线程" + Thread.CurrentThread.Name + ",已被阻塞,等待任务"); 84 autoResetEvent.WaitOne(); 85 } 86 } 87 } 88 } 89 }
这段不到100行的代码,采用的思想是,生产者消费模式,其中应用了AutoResetEvent ,从字面上看,是自动重置事件,它是EventWaitHandle的一个子类。
我们还是先来看看这段代码所要表达的意思。第8行,定义了一个文件传输队列FileTaskQueue,它用来接收生产者生产的实体,即FullTextListViewModel类型的对象。29-35行,开启了若干个线程,40行,给队列加锁,把model放到队列里,紧接着,通过Set方法,释放被阻塞的线程。此时,如果有三个线程,队列里只有1个model,那么三个线程消费一个model,必然有两个线程直接被阻塞了,那个得到model的线程执行完毕后,也被阻塞了,一直到下一个model进队,再次运行Set方法, 它们当中只有一个线程被解除了阻塞,接着干活,其它线程的继续等待。如果有足够多的model进队了,意味着活多了,这三个线程都会忙碌起来,不会阻塞。
autoResetEvent,每次调用set方法时,只有一个线程被释放。这点很关键,否则在设计多线程的程序时,会有意想不到的结果。