/// <summary>
/// 文件传输器,用来获取全文文件,自动根据全文文件数量,开启一定数量的线程,采用生产者消费模式
/// </summary>
public class FileTranser
{
private static IFileTranser Transer = new RealFileTranser();
// 文件队列
static Queue<FullTextListViewModel> FileTaskQueue = new Queue<FullTextListViewModel>();
// 为保证线程安全,使用一个锁来保护队列的访问
readonly static object locker = new object();
// 通过 autoResetEvent 给工作线程发信号
static EventWaitHandle autoResetEvent = new AutoResetEvent(false);
public static CancellationTokenSource cancel = null; static int MaxThreadCount = ;
static int MinThreadCount = ;
static int PageSize = ; static List<Thread> threads = new List<Thread>();
static bool IsRunning = false;
public static void Start(FullTextListViewModel model, int total)
{
if (cancel != null && cancel.IsCancellationRequested) return;
if (IsRunning == false || threadCount > threads.Count)
{
int startCount = threadCount - threads.Count;
IsRunning = true;
// 任务开始,启动工作线程
for (int i = ; i < startCount; i++)
{
Thread t = new Thread(Work);
t.Name = "f_" + (i + );
t.IsBackground = false;
t.Start();
threads.Add(t);
}
}
lock (locker)
{
FileTaskQueue.Enqueue(model);
}
autoResetEvent.Set();
}
public static void Cancel()
{
IsRunning = false;
threads.Clear();
if (cancel != null)
{
cancel.Cancel();
}
autoResetEvent.Set();
Logger.Log("取消获取全文的线程执行");
}
/// <summary>执行工作</summary>
static void Work()
{
while (true)
{
if (cancel.IsCancellationRequested)
{
Logger.Log("线程已取消,当前线程:" + Thread.CurrentThread.Name);
autoResetEvent.Set();
break;
}
else
{
FullTextListViewModel model = null;
lock (locker)
{
if (FileTaskQueue.Count > )
{
model = FileTaskQueue.Dequeue();
}
}
if (model != null)
{
Logger.Log("全文传输文件已经开始,当前Id:" + model.ClientTaskId + ",当前线程:" + Thread.CurrentThread.Name);
FileTranser.Transer.DoWork(model);
}
else
{
Logger.Log("线程" + Thread.CurrentThread.Name + ",已被阻塞,等待任务");
autoResetEvent.WaitOne();
}
}
}
}
}
这段不到100行的代码,采用的思想是,生产者消费模式,其中应用了AutoResetEvent ,从字面上看,是自动重置事件,它是EventWaitHandle的一个子类。
我们还是先来看看这段代码所要表达的意思。第8行,定义了一个文件传输队列FileTaskQueue,它用来接收生产者生产的实体,即FullTextListViewModel类型的对象。29-35行,开启了若干个线程,40行,给队列加锁,把model放到队列里,紧接着,通过Set方法,释放被阻塞的线程。此时,如果有三个线程,队列里只有1个model,那么三个线程消费一个model,必然有两个线程直接被阻塞了,那个得到model的线程执行完毕后,也被阻塞了,一直到下一个model进队,再次运行Set方法, 它们当中只有一个线程被解除了阻塞,接着干活,其它线程的继续等待。如果有足够多的model进队了,意味着活多了,这三个线程都会忙碌起来,不会阻塞。
autoResetEvent,每次调用set方法时,只有一个线程被释放。这点很关键,否则在设计多线程的程序时,会有意想不到的结果。