C# 中生产者和消费者模型 (一)

入队列

入队列的对象是内存块,故每次入队列前需使用 new 指令重新申请内存。

//int[] array = new int[1000];//错误
while (true)
{
    int[] array = new int[1000];//正确
    lock (queue.SyncRoot)
    {
        queue.Enqueue(array);//push queue
    }
}

线程同步

在队列安全的基础上,使用生产者和消费者线程连续访问队列,若无元素需要写入(对生产者)或无元素需要读出(对消费者),则进程休眠,即可实现生产者和消费者模型。

//生产者
Queue queue_save;
if( 有元素需要写入 )
{
    lock (queue.SyncRoot)
    {
        queue.Enqueue(array);//push queue
    }
}
else
{
    Thread.Sleep(10);
}

但连续访问效率并不够高,使用进程间同步的方法能够提高程序效率。

进程间同步有多种方式,详见参考资料【1】,这里使用 EventWaitHandle ( AutoResetEvent、ManualResetEvent )方法举例。

EventWaitHandle.Set 和 EventWaitHandle.WaitOne 方法可以分别解除阻塞/阻塞一个进程。我们让消费者线程每次无法读取数据时(队列为空)阻塞,生产者向队列中写数据时解除消费者线程阻塞。

//生产者
Queue queue_save;
private static EventWaitHandle flag = new AutoResetEvent(false);
if( 有元素需要写入 )
{
    lock (queue.SyncRoot)
    {
        queue.Enqueue(array);//push queue
    }
    flag.Set();
}
else
{
    Thread.Sleep(10);
}

//消费者
if (queue.Count > 0)
{
    lock (queue.SyncRoot)
    {
        (queue.Dequeue()).CopyTo(array, 0);
    }
}
else
{
    flag.WaitOne();
}

线程停止

简单地说,使用经典的 Thread.Abort 方法停止线程是粗暴且不优雅的,调用 Abort 方法,程序员无法知道线程停止的具体位置和时间,详见参考资料【3】。

更好的方法是程序员自己在线程内为线程定义停止接口,利用 CancellationTokenSource 类,详见参考资料【2】。当 CancellationTokenSource 类中的 Cancel 方法被调用时, CancellationTokenSource.Token.IsCancellationRequested 的值置为true,线程检测该值并退出。

下面例子来自参考资料【3】

CancellationTokenSource cts =new CancellationTokenSource();
Thread t =new Thread(() =>
{
while (true)
{
if (cts.Token.IsCancellationRequested)
{
Console.WriteLine("线程被终止!");
break;
}
Console.WriteLine(DateTime.Now.ToString());
Thread.Sleep(1000);
}
});
t.Start();
Console.ReadLine();
cts.Cancel();

参考资料
1.http://www.cnblogs.com/luminji/archive/2011/05/03/2034890.html
2.https://msdn.microsoft.com/en-us/library/system.threading.cancellationtokensource(v=vs.110).aspx
3.http://www.cnblogs.com/luminji/archive/2011/05/03/2034890.html

你可能感兴趣的:(C# 中生产者和消费者模型 (一))