C#多线程的同步与通信
2015-09-01 14:54
393 查看
C#中使用lock和Monitor控制多线程对资源的使用,最常见的生产者和消费者问题就是多线程同步和通信的经典例子。这篇文章通过例子来了解C#多线程的同步与通信。
一、关于lock和Monitor
lock可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其它线程必须等待。格式定义如下:
lock(expression) statement_block
expression代表要跟踪的对象,通常是引用。一般地,如果想保护一个类的实例,使用this;如果保护一个静态变量(如互斥代码段在一个静态方法内部),使用类名就可以了。而statement_block就是互斥段的代码。
Monitor用于多线程公用一个对象时使线程共享资源的方案。Monitor必须和一个具体的对象相关联。
二、生产者和消费者问题
假设两个线程同时维护一个队列,如果一个线程对队列中更新元素,而另外一个线程从队列中获取元素,那么我们称更新元素的线程为生产者,称获取元素的线程为消费者。
1、被操作对象
复制代码
/// <summary>;
/// 被操作对象
/// </summary>;
public class Counter
{
//更新和读取的数字
private int numberOfCounter;
//读操作可执行标记,可以防止死锁的发生
private bool readFlag = false;
public void Read()
{
//锁定后,其它读操作等待这一次读操作完成
lock (this)
{
//第一次之行为flase,进入等待
if (!readFlag)
{
try
{
//进入等待读,另一个线程写
Monitor.Wait(this);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
Console.WriteLine("消费(获取): {0}", numberOfCounter);
//重置,消费已经完成
readFlag = false;
Monitor.Pulse(this);
}
}
public void Write(int number)
{
//锁定后,其它写操作等待这一次写操作完成
lock (this)
{
//第一次readFlag为flase,跳过执行下边的写
//如果当前正在读,等待读操作执行Monitor.Pulse
if (readFlag)
{
try
{
Monitor.Wait(this);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
numberOfCounter = number;
Console.WriteLine("生产(更新): {0}", numberOfCounter);
//重置,生产已经完成
readFlag = true;
//同步通过等待Pulse来完成
Monitor.Pulse(this);
}
}
}
复制代码
2、生产者和消费者
复制代码
/// <summary>;
/// 生产者
/// </summary>
public class CounterWrite
{
Counter counter;
//生产者生产次数
int quantity = 1;
public CounterWrite(Counter box, int request)
{
//构造函数
counter = box;
quantity = request;
}
//生产者向操作对象更新信息
public void Write()
{
for (int i = 1; i <= quantity; i++)
counter.Write(i);
}
}
/// <summary>
/// 消费者
/// </summary>
public class CounterRead
{
Counter counter;
//生产者生产次数
int quantity = 1;
public CounterRead(Counter box, int request)
{
//构造函数
counter = box;
quantity = request;
}
//消费者从操作对象中获取信息
public void Read()
{
for (int i = 1; i <= quantity; i++)
counter.Read();
}
}
复制代码
3、线程操作
复制代码
Counter counter = new Counter();
CounterRead read = new CounterRead(counter, 10);
CounterWrite write = new CounterWrite(counter, 10);
Thread th1 = new Thread(new ThreadStart(read.Read));
Thread th2 = new Thread(new ThreadStart(write.Write));
th1.Start();
th2.Start();
th1.Join();
th2.Join();
Console.ReadLine();
复制代码
通过lock锁定Counter对象的引用,初始readFlag为false控制线程1等待读取:Monitor.Wait(this),线程2写入,然后更改readFlag,然后执行:Monitor.Pulse(this),通知等待队列中的线程请求对象状态已发生改变,线程1锁定this,执行读操作,然后更改readFlag,线程1和线程2交互执行写读的操作。
同时因为readFlag的存在和交替更新,避免了死锁情况的发生。
一、关于lock和Monitor
lock可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其它线程必须等待。格式定义如下:
lock(expression) statement_block
expression代表要跟踪的对象,通常是引用。一般地,如果想保护一个类的实例,使用this;如果保护一个静态变量(如互斥代码段在一个静态方法内部),使用类名就可以了。而statement_block就是互斥段的代码。
Monitor用于多线程公用一个对象时使线程共享资源的方案。Monitor必须和一个具体的对象相关联。
二、生产者和消费者问题
假设两个线程同时维护一个队列,如果一个线程对队列中更新元素,而另外一个线程从队列中获取元素,那么我们称更新元素的线程为生产者,称获取元素的线程为消费者。
1、被操作对象
复制代码
/// <summary>;
/// 被操作对象
/// </summary>;
public class Counter
{
//更新和读取的数字
private int numberOfCounter;
//读操作可执行标记,可以防止死锁的发生
private bool readFlag = false;
public void Read()
{
//锁定后,其它读操作等待这一次读操作完成
lock (this)
{
//第一次之行为flase,进入等待
if (!readFlag)
{
try
{
//进入等待读,另一个线程写
Monitor.Wait(this);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
Console.WriteLine("消费(获取): {0}", numberOfCounter);
//重置,消费已经完成
readFlag = false;
Monitor.Pulse(this);
}
}
public void Write(int number)
{
//锁定后,其它写操作等待这一次写操作完成
lock (this)
{
//第一次readFlag为flase,跳过执行下边的写
//如果当前正在读,等待读操作执行Monitor.Pulse
if (readFlag)
{
try
{
Monitor.Wait(this);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
numberOfCounter = number;
Console.WriteLine("生产(更新): {0}", numberOfCounter);
//重置,生产已经完成
readFlag = true;
//同步通过等待Pulse来完成
Monitor.Pulse(this);
}
}
}
复制代码
2、生产者和消费者
复制代码
/// <summary>;
/// 生产者
/// </summary>
public class CounterWrite
{
Counter counter;
//生产者生产次数
int quantity = 1;
public CounterWrite(Counter box, int request)
{
//构造函数
counter = box;
quantity = request;
}
//生产者向操作对象更新信息
public void Write()
{
for (int i = 1; i <= quantity; i++)
counter.Write(i);
}
}
/// <summary>
/// 消费者
/// </summary>
public class CounterRead
{
Counter counter;
//生产者生产次数
int quantity = 1;
public CounterRead(Counter box, int request)
{
//构造函数
counter = box;
quantity = request;
}
//消费者从操作对象中获取信息
public void Read()
{
for (int i = 1; i <= quantity; i++)
counter.Read();
}
}
复制代码
3、线程操作
复制代码
Counter counter = new Counter();
CounterRead read = new CounterRead(counter, 10);
CounterWrite write = new CounterWrite(counter, 10);
Thread th1 = new Thread(new ThreadStart(read.Read));
Thread th2 = new Thread(new ThreadStart(write.Write));
th1.Start();
th2.Start();
th1.Join();
th2.Join();
Console.ReadLine();
复制代码
通过lock锁定Counter对象的引用,初始readFlag为false控制线程1等待读取:Monitor.Wait(this),线程2写入,然后更改readFlag,然后执行:Monitor.Pulse(this),通知等待队列中的线程请求对象状态已发生改变,线程1锁定this,执行读操作,然后更改readFlag,线程1和线程2交互执行写读的操作。
同时因为readFlag的存在和交替更新,避免了死锁情况的发生。
相关文章推荐
- C#依据word模版动态生成文档
- C# 操作注册安装、开启、停止、卸载服务
- C#------泛型集合、字典
- C#.NET实现基于Lumisoft的邮件收发功能
- C# virtual和override
- 问题:C#根据生日计算属相;结果:C#实现根据年份计算生肖属相的方法
- C#------StreamWriter:写入文件
- C#--简单文件下载器
- C#------封装文件拷贝方法
- C#中DataGridView常用操作实例小结
- C#运算符笔记
- C#实现 word、pdf、ppt 转为图片
- C#:通过Window API接口实现WiFi
- 学习C#小问题积累
- C#学习笔记----.net操作进程
- C#创建windows服务程序,并制作windows服务安装包。
- 预定义的类型“Microsoft.CSharp.RuntimeBinder.Binder”未定义或未导入
- C#延时
- C# 配置错误定义了重复的“system.web.extensions/scripting/scriptResourceHandler”节
- C#时间相关