互斥与同步
今天我们来看互斥与同步,互斥与同步一直线程操作方面的至关重要的一部份.首先来看下他们之间的概念.
互斥和同步都是在并发环境下编程特有的问题.并发是指宏观上多个线程同时进行而某一时刻只有一个线程运行,即宏观并行微观串行.大家应该都知道时间片的概念,上面的话应该很容易理解的.并发环境下,不能有两个以上(包括两个)某类特殊的资源,就叫互斥.这类特殊资源也被称为临界资源,例如字符串缓冲区,文件,实例对象等.同步是指多个线程互相通信,互相等待从而使进程按照一定顺序往前推进.其实特殊的资源应该就是经常用到的变量与对象只不过在某一时刻只能由一个线程操作他.
1.互斥
.NET公共运行库提供了几种方法实现对临界资源的互斥访问.
首先看Monitor这个类

[ComVisible(true)]
public static class Monitor
{
[SecuritySafeCritical]
public static void Enter(object obj);
[SecuritySafeCritical]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static void Exit(object obj);
[SecuritySafeCritical]
public static void Pulse(object obj);
public static bool TryEnter(object obj);
[SecuritySafeCritical]
public static bool Wait(object obj);

.....
}

这几个方法简单介绍,Enter获取对象锁(参数obj),Exit释放对象锁,Pulse通知等待队列中的线程锁定对象状态的更改,TryEnter尝试获取对象锁,Wait释放对象上的锁并阻止当前线程,直到它重新获取该锁。
下面来看个例子:

 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6  7 namespace ConsoleApplication8 8 { 9     class Program10     {11         int x = 100;12         static void Main(string[] args)13         {14             Program p = new Program();15             Thread t1 = new Thread(p.MyWork1);16             Thread t2 = new Thread(p.MyWork2);17             t1.Start();18             t2.Start();19         }20         void MyWork1()21         {22             try23             {24                 Monitor.Enter(this);25                 while (true)26                 {27                    28                     if (x < 1) { break; }29                     x--;30                     Console.WriteLine("调用MyWork1()");31                 }32             }33             finally34             {35                 Monitor.Exit(this);36             }37         }38         void MyWork2()39         {40            41             try42             {43                 Monitor.Enter(this);44                 while (true)45                 {46 47                     if (x < 1) { break; }48                     x = x - 2;49                     Console.WriteLine("调用MyWork2()");50 51                 }52             }53             finally54             {55                 Monitor.Exit(this);56             }57         }58     }59 }

输出结果为

结果全为MyWork1的调用,如果将 Monitor.Enter(this)和Monitor.Exit(this)注释起来的话,在运行的话,就会出现MyWork1和Mywork2都在运行了.还有可以将MyWork1中的x<1改成10那运行结果为

可见MyWork1先运行了,当他释放完锁之后MyWork2获取到对象所之后才运行.从这个例子我们可以看出 Monitor.Enter(this)和Monitor.Exit(this)的用法.按照我的理解:Monitor.Enter(this)会一直等待,直到获到对象锁.对于这个猜测,我们可以在MyWork2的 Monitor.Enter(this)加一句输出语句.可以看这个输出结果会在控制台上.

将上面的例子稍加更改,来重点说下TryEnter,Wait,Pulse这三函数的用法及相关解释.

先看代码

 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6  7 namespace ConsoleApplication8 8 { 9     class Program10     {11         int x = 100;12         static void Main(string[] args)13         {14             Program p = new Program();15             Thread t1 = new Thread(p.MyWork1);16             Thread t2 = new Thread(p.MyWork2);17             t1.Start();18            // Thread.Sleep(2000);19             t2.Start();20         }21         void MyWork1()22         {23             try24             {25                 Monitor.Enter(this);26                // Monitor.Pulse(this);27                 Monitor.Wait(this);28                 while (true)29                 {30                    31                     if (x < 1) { break; }32                     x--;33                     Console.WriteLine("调用MyWork1()");34                 }35             }36             finally37             {38                 try39                 {40                     Monitor.Exit(this);41                 }42                 catch (Exception e)43                 {44                     Console.WriteLine("MyWork1异常退出,信息为");45                     Console.WriteLine(e.Message); 46                 }47             }48         }49         void MyWork2()50         {51 52             try53             {54                 //Monitor.Enter(this)55                 if (Monitor.TryEnter(this))56                 {57                     Monitor.Pulse(this);58                     while (true)59                     {60 61                         if (x < 10) { break; }62                         x = x - 2;63                         Console.WriteLine("调用MyWork2()");64 65                     }66                 }67             }68             finally69             {70                 try71                 {72                     73                     Monitor.Exit(this);74                     75                 }76                 catch (Exception e)77                 {78                     Console.WriteLine("MyWork2异常退出,信息为");79                     Console.WriteLine(e.Message);80                 }81             }82         }83     }84 }

运行结果如下图

程序的执行流程为,首先MyWork1获得对象锁,然后调用Wait方法释放锁,阻止当前线程,直到再次获得对象锁为止.MyWork2此时运行,调用TryEnter这个方法,注意了,这个方法和Enter有很大的区别的,他会立即返回结果.不像Enter会一直在那等.因为之前对象锁已经释放,所以返回真.如果将Wait方法注释的话,MyWork2里的TryEnter方法将立即返回假,程序异常结束.接着调用Pulse方法,通知其他线程锁定对象状态的更改,当X<10对象锁释放,而一直处于等待MyWork1将再次获得对象锁.执行相应的动作.这里我要说下Pulse这个方法,如果MyWork2将这个方法注释,那么MyWork1这个线程将一直处于等待状态.一直获不到对象锁的.

通过这两个例子,应该对Monitor这个类比较了解了.

还有重要的东西,如果是类的静态方法或属性,此时就要锁定该类型了,这个地方要注意的,在这我也给各例子:

结果这里就不给了;

 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6  7 namespace ConsoleApplication9 8 { 9     class Program10     {11         static void Main(string[] args)12         {13             Thread t1 = new Thread(MyWork1);14             Thread t2 = new Thread(MyWork2);15             t1.Start();16             t2.Start();17         }18         static void MyWork1()19         {20             Monitor.Enter(typeof(Program));21             for (int i = 1; i < 20; i++)22             {23                 Console.WriteLine("MyWork1()");24                 Thread.Sleep(10);25             }26             Monitor.Exit(typeof(Program));27         }28         static void MyWork2()29         {30             Monitor.Enter(typeof(Program));31             for (int i = 1; i < 20; i++)32             {33                 Console.WriteLine("MyWork2()");34                 Thread.Sleep(10);35             }36             Monitor.Exit(typeof(Program));37         }38     }39 }

下面看.Net比较简单操作同步的方法

如果临界区跨越整个方法,则可以通过将using System.Runtime.CompilerServices的MethodImplAttribute放置在方法并在MethodImplAttribute德构造函数中制定MethodImplOptions.Synchronized值来实现上述锁定功能.对于上面的例子,可作如下更改

[MethodImpl(MethodImplOptions.Synchronized)]
   static void MyWork1()
   {
            //Monitor.Enter(typeof(Program));
            for (int i = 1; i < 20; i++)
            {
                Console.WriteLine("MyWork1()");
                Thread.Sleep(10);
            }
           // Monitor.Exit(typeof(Program));
   }

MyWork2也一样,运行结果一样.

lock关键字,lock(expression)必须是引用类型.

可将上面的例子作如下更改

static   void MyWork1()
{
       lock (typeof(Program))
      {
                for (int i = 1; i < 20; i++)
                {
                    Console.WriteLine("MyWork1()");
                    Thread.Sleep(10);
                }
      }
 }

这里注意下MyWork1是静态方法,所以锁定类型,大多时候我们都是用lock(this);

ReaderWriterLock这个类有着重多的优点,比如开销低,支持超时等等.看下这个类的定义以及常用的方法

[ComVisible(true)]
public sealed class ReaderWriterLock : CriticalFinalizerObject
{
  public void AcquireReaderLock(int millisecondsTimeout);
   public void AcquireWriterLock(int millisecondsTimeout);
   public LockCookie ReleaseLock();
   public void ReleaseReaderLock();
   public void ReleaseWriterLock();
......
}

请看下面的例子

 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6  7 namespace ConsoleApplication10 8 { 9     class Program10     {11         int value = 100;12         ReaderWriterLock rwl = new ReaderWriterLock();13         static void Main(string[] args)14         {15             Program p = new Program();16             Thread tr1 = new Thread(p.Read);17             Thread tw1 = new Thread(p.Write);18             tr1.Start();19             tw1.Start();20 21 22         }23         void Read() 24         {25             rwl.AcquireReaderLock(Timeout.Infinite);26             Console.WriteLine("Read开始读");27             Thread.Sleep(10);28             Console.WriteLine("Read读出的Value值为{0}", value);29             Console.WriteLine("Read结束读");30             rwl.ReleaseReaderLock();31         }32         void Write() 33         {34             rwl.AcquireWriterLock(Timeout.Infinite);35             Console.WriteLine("Write开始写");36             Thread.Sleep(10);37             value++;38             Console.WriteLine("Write写完后的Value值为{0}", value);39             Console.WriteLine("Write结束写");40             rwl.ReleaseReaderLock();41         }42     }43 }

结果为

大家可以看到读的时候锁定对象,无法写入,换两个线程的开始位置,就可以得到写的时候锁定对象,读的操作无法读取.

这里有两个方法有点意思的public LockCookie UpgradeToWriterLock(int millisecondsTimeout)和public LockCookie UpgradeToWriterLock(TimeSpan timeout)这个方法是读写线程锁的转换.大家可以自己尝试下.

好了,今天到这.关于互斥还有些内容,下次再讨论!

转载于:https://www.cnblogs.com/enuo/archive/2011/12/09/2281841.html

C#线程从陌生到熟悉(4)相关推荐

  1. 从陌生到熟悉,再变成最熟悉的陌生人:伤感日志

    从陌生到熟悉,再变成最熟悉的陌生人:伤感日志 - 从陌生到熟悉,再变成最熟悉的陌生人:伤感日志 既然,牵手,为何,分手? 既然,分手,为何,后悔? 既然,爱过,为何,怨恨? 既然,怨恨,为何痛苦? 曾 ...

  2. 【微信小程序项目实践总结】30分钟从陌生到熟悉

    前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05-日历组件的实现 4. 微信小程序开发04-打造自 ...

  3. 【BootStrap】陌生=》熟悉=》惊喜

    最近一直在写页面,上周真的是写完一个又一个的页面.通过写这些页面,也是接触到了BootStrap中很多的常用插件,类似我们之前所说的控件.下面就来看看BootStrap中会给我们带来哪些眼前一亮的插件 ...

  4. 福禄克FLUKE LinkIQ智能链路通(LIQ-100,LIQ-KIT)从开箱到设置、从陌生到熟悉

    关于LinkIQ™智能链路通,是不是已经有各种机会耳闻它了.现在小福的同事实际操做一遍,告诉你LinkIQ到底有多能干! 1  技术概览 为了应对不同的需求,新款LinkIQ™智能链路通特别设计了几套 ...

  5. 怎样才算熟悉python-怎么样才算是精通 Python?

    更新: 之前在组里分享过,后端那些事,有兴趣可以看看. 要想精通python,写的代码首先得pythonic,自己闭门造车肯定不行,肯定需要研读牛B的开源代码,在这过程中会遇到python的许多高阶用 ...

  6. 线程模型、pthread 系列函数 和 简单多线程服务器端程序

    一.线程有3种模型,分别是N:1用户线程模型,1:1核心线程模型和N:M混合线程模型,posix thread属于1:1模型. (一).N:1用户线程模型 "线程实现"建立在&qu ...

  7. [转]COM线程模型-套间

    [转]COM线程模型-套间 COM线程模型-套间 来源: http://blog.csdn.net/crybird/archive/2008/10/11/3057067.aspx 查找了好多资料,终于 ...

  8. 深入理解 Java 锁与线程阻塞

    相信大家对线程锁和线程阻塞都很了解,无非就是 synchronized, wait/notify 等, 但是你有仔细想过 Java 虚拟机是如何实现锁和阻塞的呢?它们之间又有哪些联系呢?如果感兴趣的话 ...

  9. Android子线程更新UI的方法总结

    消息机制,对于Android开发者来说,应该是非常熟悉.对于处理有着大量交互的场景,采用消息机制,是再好不过了.有些特殊的场景,比如我们都知道,在Android开发中,子线程不能更新UI,而主线程又不 ...

最新文章

  1. 元素的   is_enable()、is_displayed()和is_selected()
  2. oracle 11g 大小,修改oracle 11GR2归档模式和归档目录及大小-Oracle
  3. Python中list、set和tuple
  4. python 中五种常用的数据类型
  5. 史上最详细阿里云服务器上Docker部署vue项目 实战 每一步都带详细图解!!!
  6. Spring系列教程八: Spring实现事务的两种方式
  7. FLY主题下载插件兼容php7适配emlog6.1.1
  8. 图神经网络中可能用到的11种距离, 小结
  9. NVIDIA-SMI has failed because it couldn’t communicate with the NVIDIA driver.
  10. cxGrid 怎样才能让不自动换行 WordWrap:=false
  11. go的匿名函数和闭包
  12. 使用Uchihash处理恶意软件中的嵌入式哈希
  13. Mall谷粒商城(基础篇的开发)
  14. 基于JavaWeb的微博系统设计与实现
  15. 你们要的日文AI实时字幕来了,谷歌浏览器VS小白浏览器AI大比拼
  16. 金刚石结构的各向异性
  17. 苹果邮箱怎么登录qq邮箱_qq邮箱app下载安装-手机QQ邮箱2020下载v6.1.0 官方安卓版...
  18. 绅聚科技推出首款国产化VoIP专用芯片A1010
  19. gamemaker studio socket例子
  20. error pulling image configuration:XXX net/http: TLS handshake timeout

热门文章

  1. beego 显示html文件,[Beego] 内置的模板函数(不同格式的字符串和html的互转)
  2. gitlab合并分支_GitLab-参考问题
  3. getprivateprofilestring读不到数据_从零到千万用户,我是如何一步步优化MySQL数据库的?...
  4. Java类加载机制:双亲委托模型
  5. Android Apt失效:找不到Apt生成的对应类
  6. 开发日记-20190527 关键词 ubuntu无线网卡驱动安装
  7. lucene正向索引(续)——域(Field)的元数据信息在.fnm里,在倒排表里,利用跳跃表,有利于大大提高搜索速度。...
  8. TokuDB介绍——本质是分形树(一个叶子4MB)+缓存减少写操作
  9. JavaScript EventLoop
  10. uwsgi gevent