收藏 分享(赏)

多线程学习(三) 生产者和消费者.doc

上传人:sjmd695 文档编号:9485817 上传时间:2019-08-09 格式:DOC 页数:11 大小:39KB
下载 相关 举报
多线程学习(三) 生产者和消费者.doc_第1页
第1页 / 共11页
多线程学习(三) 生产者和消费者.doc_第2页
第2页 / 共11页
多线程学习(三) 生产者和消费者.doc_第3页
第3页 / 共11页
多线程学习(三) 生产者和消费者.doc_第4页
第4页 / 共11页
多线程学习(三) 生产者和消费者.doc_第5页
第5页 / 共11页
点击查看更多>>
资源描述

1、多线程学习(三) 生产者和消费者C#多线程学习(三) 生产者和消费者 前面说过,每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数。这可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生。C#提供了一个关键字 lock,它可以把一段代码定义为互斥段(critical section) ,互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。在 C#中,关键字 lock 定义如下:lock(expression) statement_block expression 代表你希望跟踪的对象,通常是对象引用。如

2、果你想保护一个类的实例,一般地,你可以使用 this;如果你想保护一个静态变量(如互斥代码段在一个静态方法内部) ,一般使用类名就可以了。而 statement_block 就是互斥段的代码,这段代码在一个时刻内只可能被一个线程执行。 下面是一个使用 lock 关键字的典型例子,在注释里说明了 lock 关键字的用法和用途。示例如下:C# codeusing System;using System.Threading;namespace ThreadSimpleinternal class Account int balance;Random r = new Random(); interna

3、l Account(int initial) balance = initial; internal int Withdraw(int amount) if (balance = amount) Thread.Sleep(5);balance = balance - amount;return amount; else return 0; internal void DoTransactions() for (int i = 0; i 100; i+) Withdraw(r.Next(-50, 100); internal class Test static internal Thread t

4、hreads = new Thread10;public static void Main() Account acc = new Account (0);for (int i = 0; i 10; i+) Thread t = new Thread(new ThreadStart(acc.DoTransactions);threadsi = t;for (int i = 0; i 10; i+) threadsi.Name=i.ToString();for (int i = 0; i 10; i+) threadsi.Start();Console.ReadLine();Monitor 类锁

5、定一个对象当多线程公用一个对象时,也会出现和公用代码类似的问题,这种问题就不应该使用 lock 关键字了,这里需要用到System.Threading 中的一个类 Monitor,我们可以称之为监视器,Monitor 提供了使线程共享资源的方案。 Monitor 类可以锁定一个对象,一个线程只有得到这把锁才可以对该对象进行操作。对象锁机制保证了在可能引起混乱的情况下一个时刻只有一个线程可以访问这个对象。Monitor 必须和一个具体的对象相关联,但是由于它是一个静态的类,所以不能使用它来定义对象,而且它的所有方法都是静态的,不能使用对象来引用。下面代码说明了使用 Monitor 锁定一个对象的

6、情形:Queue oQueue=new Queue();Monitor.Enter(oQueue);/ 现在 oQueue 对象只能被当前线程操纵了Monitor.Exit(oQueue);/ 释放锁 如上所示,当一个线程调用 Monitor.Enter()方法锁定一个对象时,这个对象就归它所有了,其它线程想要访问这个对象,只有等待它使用 Monitor.Exit()方法释放锁。为了保证线程最终都能释放锁,你可以把 Monitor.Exit()方法写在 try-catch-finally 结构中的finally 代码块里。对于任何一个被 Monitor 锁定的对象,内存中都保存着与它相关的一些

7、信息:其一是现在持有锁的线程的引用;其二是一个预备队列,队列中保存了已经准备好获取锁的线程;其三是一个等待队列,队列中保存着当前正在等待这个对象状态改变的队列的引用。当拥有对象锁的线程准备释放锁时,它使用 Monitor.Pulse()方法通知等待队列中的第一个线程,于是该线程被转移到预备队列中,当对象锁被释放时,在预备队列中的线程可以立即获得对象锁。 下面是一个展示如何使用 lock 关键字和 Monitor 类来实现线程的同步和通讯的例子,也是一个典型的生产者与消费者问题。这个例程中,生产者线程和消费者线程是交替进行的,生产者写入一个数,消费者立即读取并且显示(注释中介绍了该程序的精要所在

8、) 。用到的系统命名空间如下:using System;using System.Threading; 首先,定义一个被操作的对象的类 Cell,在这个类里,有两个方法:ReadFromCell()和 WriteToCell。消费者线程将调用 ReadFromCell()读取 cellContents 的内容并且显示出来,生产者进程将调用WriteToCell()方法向 cellContents 写入数据。示例如下:C# codepublic class Cellint cellContents; bool readerFlag = false;public int ReadFromCell(

9、 )lock(this) if (!readerFlag) try/等待 WriteToCell 方法中调用Monitor.Pulse()方法Monitor.Wait(this);catch (SynchronizationLockException e)Console.WriteLine(e);catch (ThreadInterruptedException e)Console.WriteLine(e);Console.WriteLine(“Consume: 0“,cellContents);readerFlag = false;/重置 readerFlag 标志,表示消费行为已经完成Mo

10、nitor.Pulse(this); /通知 WriteToCell()方法(该方法在另外一个线程中执行,等待中)return cellContents;public void WriteToCell(int n)lock(this)if (readerFlag)tryMonitor.Wait(this);catch (SynchronizationLockException e)/当同步方法(指 Monitor 类除 Enter 之外的方法)在非同步的代码区被调用Console.WriteLine(e);catch (ThreadInterruptedException e)/当线程在等待状

11、态的时候中止 Console.WriteLine(e);cellContents = n;Console.WriteLine(“Produce: 0“,cellContents);readerFlag = true; Monitor.Pulse(this); /通知另外一个线程中正在等待的ReadFromCell()方法下面定义生产者类 CellProd 和消费者类 CellCons ,它们都只有一个方法 ThreadRun(),以便在 Main()函数中提供给线程的ThreadStart 代理对象,作为线程的入口。C# codepublic class CellProdCell cell;

12、/ 被操作的 Cell 对象int quantity = 1; / 生产者生产次数,初始化为 1 public CellProd(Cell box, int request)cell = box; quantity = request; public void ThreadRun( )for(int looper=1; looper=quantity; looper+)cell.WriteToCell(looper); /生产者向操作对象写入信息public class CellConsCell cell; int quantity = 1; public CellCons(Cell box,

13、 int request)cell = box; quantity = request; public void ThreadRun( )int valReturned;for(int looper=1; looper=quantity; looper+)valReturned=cell.ReadFromCell( );/消费者从操作对象中读取信息 然后在下面这个类 MonitorSample 的 Main()函数中,我们要做的就是创建两个线程分别作为生产者和消费者,使用CellProd.ThreadRun()方法和 CellCons.ThreadRun()方法对同一个Cell 对象进行操作。

14、C# codepublic class MonitorSamplepublic static void Main(String args)int result = 0; /一个标志位,如果是 0 表示程序没有出错,如果是 1 表明有错误发生Cell cell = new Cell( ); /下面使用 cell 初始化 CellProd 和 CellCons 两个类,生产和消费次数均为 20 次CellProd prod = new CellProd(cell, 20); CellCons cons = new CellCons(cell, 20); Thread producer = new

15、Thread(new ThreadStart(prod.ThreadRun);Thread consumer = new Thread(new ThreadStart(cons.ThreadRun);/生产者线程和消费者线程都已经被创建,但是没有开始执行 tryproducer.Start( );consumer.Start( ); producer.Join( ); consumer.Join( );Console.ReadLine();catch (ThreadStateException e)Console.WriteLine(e); result = 1; catch (ThreadI

16、nterruptedException e)Console.WriteLine(e); result = 1; /尽管 Main()函数没有返回值,但下面这条语句可以向父进程返回执行结果Environment.ExitCode = result;在上面的例程中,同步是通过等待 Monitor.Pulse()来完成的。首先生产者生产了一个值,而同一时刻消费者处于等待状态,直到收到生产者的“脉冲(Pulse)”通知它生产已经完成,此后消费者进入消费状态,而生产者开始等待消费者完成操作后将调用Monitor.Pulese()发出的“脉冲” 。它的执行结果很简单:Produce: 1Consume: 1Produce: 2Consume: 2Produce: 3Consume: 3.Produce: 20Consume: 20 事实上,这个简单的例子已经帮助我们解决了多线程应用程序中可能出现的大问题,只要领悟了解决线程间冲突的基本方法,很容易把它应用到比较复杂的程序中去。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报