• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C#教程 > C#实现多线程的同步方法实例分析

C#实现多线程的同步方法实例分析

作者:xugang 字体:[增加 减小] 来源:互联网 时间:2017-05-28

xugang 通过本文主要向大家介绍了c#多线程编程实例,c#多线程参考与实例,c#多线程实例,c#线程实例,c#线程池实例等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

本文主要描述在C#中线程同步的方法。线程的基本概念网上资料也很多就不再赘述了。直接接入 主题,在多线程开发的应用中,线程同步是不可避免的。在.Net框架中,实现线程同步主要通过以下的几种方式来实现,在MSDN的线程指南中已经讲了几种,这里结合作者实际中用到的方式一起说明一下。

1. 维护自由锁(InterLocked)实现同步

2. 监视器(Monitor)和互斥锁(lock)

3. 读写锁(ReadWriteLock)

4. 系统内核对象

1) 互斥(Mutex), 信号量(Semaphore), 事件(AutoResetEvent/ManualResetEvent)

2) 线程池

除了以上的这些对象之外实现线程同步的还可以使用Thread.Join方法。这种方法比较简单,当你在第一个线程运行时想等待第二个线程执行结果,那么你可以让第二个线程Join进来就可以了。

自由锁(InterLocked)

对一个32位的整型数进行递增和递减操作来实现锁,有人会问为什么不用++或--来 操作。因为在多线程中对锁进行操作必须是原子的,而++和--不具备这个能力。InterLocked类还提供了两个另外的函数Exchange, CompareExchange用于实现交换和比较交换。Exchange操作会将新值设置到变量中并返回变量的原来值: int oVal = InterLocked.Exchange(ref val, 1)。

监视器(Monitor)

在MSDN中对Monitor的描述是: Monitor 类通过向单个线程授予对象锁来控制对对象的访问。

Monitor类是一个静态类因此你不能通过实例化来得到类的对象。Monitor 的成员可以查看MSDN,基本上Monitor的效果和lock是一样的,通过加锁操作Enter设置临界区,完成操作后使用Exit操作来释放对象锁。 不过相对来说Monitor的功能更强,Moniter可以进行测试锁的状态,因此你可以控制对临界区的访问选择,等待or离开, 而且Monitor还可以在释放锁之前通知指定的对象,更重要的是使用Monitor可以跨越方法来操作。Monitor提供的方法很少就只有获取锁的方 法Enter, TryEnter;释放锁的方法Wait, Exit;还有消息通知方法Pulse, PulseAll。经典的Monitor操作是这样的:

// 通监视器来创建临界区 
static public void DelUser(string name)
{
  try
  {
  // 等待线程进入 
  Monitor.Enter(Names);
  Names.Remove(name);
  Console.WriteLine("Del: {0}", Names.Count);
  Monitor.Pulse(Names);
  }
  finally
  {
  // 释放对象锁 
  Monitor.Exit(Names);
  }
 } 
}
</div>

其中Names是一个List, 这里有一个小技巧,如果你想声明整个方法为线程同步可以使用方法属性:

// 通过属性设置整个方法为临界区 
[MethodImpl(MethodImplOptions.Synchronized)] 
static public void AddUser(string name) 
{ 
 Names.Add(name); 
 Console.WriteLine("Add: {0}",Names.Count); 
}
</div>

对于Monitor的使用有一个方法是比较诡异的,那就是Wait方法。在MSDN中对Wait的描述是: 释放对象上的锁以便允许其他线程锁定和访问该对象。

这里提到的是先释放锁,那么显然我们需要先得到锁,否则调用Wait会出现异常,所 以我们必须在Wait前面调用Enter方法或其他获取锁的方法,如lock,这点很重要。对应Enter方法,Monitor给出来另一种实现 TryEnter。这两种方法的主要区别在于是否阻塞当前线程,Enter方法在获取不到锁时,会阻塞当前线程直到得到锁。不过缺点是如果永远得不到锁那 么程序就会进入死锁状态。我们可以采用Wait来解决,在调用Wait时加入超时时限就可以。

if (Monitor.TryEnter(Names))
{
 Monitor.Wait(Names, 1000); // !! 
 Names.Remove(name); 
 Console.WriteLine("Del: {0}", Names.Count);
 Monitor.Pulse(Names); 
}
</div>

互斥锁(lock)

lock关键字是实现线程同步的比较简单的方式,其实就是设置一个临界区。在 lock之后的{...}区块为一个临界区,当进入临界区时加互斥锁,离开临界区时释放互斥锁。MSDN对lock关键字的描述是: lock 关键字可将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。

具体例子如下:

static public void ThreadFunc(object name)
{
 string str = name as string;
 Random rand = new Random();
 int count = rand.Next(100, 200);
 for (int i = 0; i < count; i++)
 {
 lock (NumList)
 {
 NumList.Add(i);
 Console.WriteLine("{0} {1}", str, i);
 }
 }
}
</div>

对lock的使用有几点建议:对实例锁定lock(this),对静态变量锁定lock(typeof(val))。lock的对象访问权限最好是private,否则会出现失去访问控制现象。

读写锁(ReadWriteLock)

读写锁的出现主要是在很多情况下,我们读资源的操作要多于写资源的操作。但是如果每 次只对资源赋予一个线程的访问权限显然是低效的,读写锁的优势是同时可以有多个线程对同一资源进行读操作。因此在读操作比写操作多很多,并且写操作的时间 很短的情况下使用读写锁是比较有效率的。读写锁是一个非静态类所以你在使用前需要先声明一个读写锁对象:

static private ReaderWriterLock _rwlock = new ReaderWriterLock();
</div>

读写锁是通过调用AcquireReaderLock,ReleaseReaderLock,AcquireWriterLock,ReleaseWriterLock来完成读锁和写锁控制的

static public void ReaderThread(int thrdId) 
{ 
  try 
  { // 请求读锁,如果100ms超时退出 
  _rwlock.AcquireReaderLock(10); 
  try 
  { 
   int inx = _rand.Next(_list.Count); 
   if (inx < _list.Count) 
   Console.WriteLine("{0}thread {1}", thrdId, _list[inx]); 
  } 
  finally 
  {
   _rwlock.ReleaseReaderLock(); 
  } 
  } 
  catch (ApplicationException) // 如果请求读锁失败 
  { 
  Console.WriteLine("{0}thread get reader lock out time!", thrdId); 
  } 
 } 
 static public void WriterThread() 
 { 
  try 
  {
  // 请求写锁 
  _rwlock.AcquireWriterLock(100); 
  try 
  { 
   string val = _rand.Next(200).ToString(); 
   _list.Add(val); // 写入资源 
   Console.WriteLine("writer thread has written {0}", val); 
  } 
  finally 
  { // 释放写锁 
   _rwlock.ReleaseWriterLock(); 
  } 
  } 
  catch (ApplicationException) 
  { 
  Console.WriteLine("Get writer thread lock out time!"); 
  } 
}
</div>

如果你想在读的时候插入写操作请使用UpgradeToWriterLock和DowngradeFromWriterLock来进行操作,而不是释放读锁。

static private void UpgradeAndDowngrade(int thrdId) 
{ 
 try 
 { 
 _rwlock.AcquireReaderLock(10); 
 try 
 { 
  try 
  {
  // 提升读锁到写锁 
  LockCookie lc = _rwlock.UpgradeToWriterLock(100);
  try
  {
  string val = _rand.Next(500).ToString();
  _list.Add(val); Console.WriteLine
("Upgrade Thread{0} add {1}", thrdId, val); 
  } 
  finally
  { // 下降写锁 
  _rwlock.DowngradeFromWriterLock(ref lc); 
  } 
  } 
  catch (ApplicationException)
  { 
  Console.WriteLine("{0}thread upgrade reader lock failed!", thrdId); 
  } 
 } 
 finally 
 {
  // 释放原来的读锁 
  _rwlock.ReleaseReaderLock();
 }
 } 
 catch (ApplicationException) 
 { 
 Console.WriteLine("{0}thread get reader lock out time!", thrdId);
 }
}
</div>

这里有一点要注意的就是读锁和写锁的超时等待时间间隔的设置。通常情况下设置写锁的等待超时要比读锁的长,否则会经常发生写锁等待失败的情况。

系统内核对象 互斥对象(Mutex)

互斥对象的作用有点类似于监视器对象,确保一个代码块在同一时刻只有一个线程在执 行。互斥对象和监视器对象的主要区别就是,互斥对象一般用于跨进程间的线程同步,而监视器对象则用于进程内的线程同步。互斥对象有两种:一种是命名互斥; 另一种是匿名互斥。在跨进程中使用到的就是命名互斥,一个已命名的互斥就是一个系统级的互斥,它可以被其他进程所使用,只要在创建互斥时指定打开互斥的名 称就可以。在.Net中互斥是通过Mutex类来实现。

其实对于OpenExisting函数有两个重载版本,

Mutex.OpenExisting (String)

</div>
Mutex.OpenExisting (String, MutexRights)
</div>

对于默认的第一个函数其实是实现了第二个函数 MutexRights.Synchronize|MutexRights.Modify操作。

由于监视器的设计是基于.Net框架,而Mutex类是系统内核对象封装了win32的一个内核结构来实现互斥,并且互斥操作需要请求中断来完成,因此在进行进程内线程同步的时候性能上要比互斥要好。

典型的使用Mutex同步需要完成三个步骤的操作:1.打开或者创建一个Mutex实例;2.调用Wa

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C#多线程编程详解
  • C#制作多线程处理强化版网络爬虫
  • C#模式画刷HatchBrush用法实例
  • C#纹理画刷TextureBrush用法实例
  • c#实现多线程局域网聊天系统
  • C#选择法排序实例分析
  • C#使用动态规划解决0-1背包问题实例分析
  • C#多线程编程中的锁系统(四):自旋锁
  • C#多线程编程中的锁系统(三)
  • C#多线程编程中的锁系统(二)

相关文章

  • 2017-05-28WinForm自定义函数FindControl实现按名称查找控件
  • 2017-05-28WinForm DataGridView控件隔行变色的小例子
  • 2017-05-28C# 中的动态创建组件(属性及事件)的实现思路及方法
  • 2017-05-28C#实现几十万级数据导出Excel及Excel各种操作实例
  • 2017-05-28浅谈static a[n*m]={0};中static的作用
  • 2017-05-28C#读取XML中元素和属性值的实现代码
  • 2017-05-28C#动态创建Access数据库及密码的方法
  • 2017-05-28C#读取目录下所有指定类型文件的方法
  • 2017-05-28C#重载运算符详解
  • 2017-05-28C#判断指定文件是否是只读的方法

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • C#使用带like的sql语句时防sql注入的方法
    • C# Fiddler插件实现网站离线浏览功能
    • c#动态加载卸载DLL的方法
    • 浅谈C#.NET、JavaScript和JSON
    • C#远程发送和接收数据流生成图片的方法
    • C#图片处理3种高级应用
    • c#后台线程访问前台控件并显示信息示例
    • C#简单的特殊输出实例
    • DevExpress实现自定义GridControl中按钮文字内容的方法
    • C#裁剪,缩放,清晰度,水印处理操作示例

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有