介绍

平常在多线程开发中,总避免不了线程同步。本篇就对net多线程中的锁系统做个简单描述。
 
目录
一:lock、Monitor
 
     1:基础。
 
     2: 作用域。
 
     3:字符串锁。
 
     4:monitor使用
 
二:mutex
 
三:Semaphore
 
四:总结
 
一:lock、Monitor
1:基础
 
Lock是Monitor语法糖简化写法。Lock在IL会生成Monitor。
 
       //======Example 1=====
            string obj = "helloworld";
            lock (obj)
            {
                Console.WriteLine(obj);
            }
            //lock  IL会编译成如下写法
            bool isGetLock = false;
            Monitor.Enter(obj, ref isGetLock);
            try
            {
                Console.WriteLine(obj);
            }
            finally
            {
                if (isGetLock)
                {
                    Monitor.Exit(obj);
                }
            }
 
isGetLock参数是Framework  4.0后新加的。 为了使程序在所有情况下都能够确定,是否有必要释放锁。例: Monitor.Enter拿不到锁
 
Monitor.Enter 是可以锁值类型的。锁时会装箱成新对象。
 
2:作用域
 
     一:Lock是只能在进程内锁,不能跨进程,这个无需多说。
 
     二:关于对type类型的锁。如下:
 
复制代码
   //======Example 2=====
            new Thread(new ThreadStart(() => {
                lock (typeof(int))
                {
                    Thread.Sleep(10000);
                    Console.WriteLine("Thread1释放");
                }
            })).Start();
            Thread.Sleep(1000);
            lock(typeof(int))
            {
                Console.WriteLine("Thread2释放");
            }
 
 
 
 
我们在来看个例子。
 
 
  //======Example 3=====
            Console.WriteLine(DateTime.Now);
            AppDomain appDomain1 = AppDomain.CreateDomain("AppDomain1");
            LockTest Worker1 = (LockTest)appDomain1.CreateInstanceAndUnwrap(
             Assembly.GetExecutingAssembly().FullName,
             "ConsoleApplication1.LockTest");
            Worker1.Run();
 
            AppDomain appDomain2 = AppDomain.CreateDomain("AppDomain2");
            LockTest Worker2 = (LockTest)appDomain2.CreateInstanceAndUnwrap(
            Assembly.GetExecutingAssembly().FullName,
            "ConsoleApplication1.LockTest");
            Worker2.Run();
/// <summary>
    /// 跨应用程序域边界或远程访问时需要继承MarshalByRefObject
    /// </summary>
    public class LockTest : MarshalByRefObject
    {
        public void Run()
        {
            lock (typeof(int))
            {
                Thread.Sleep(10000);
                Console.WriteLine(AppDomain.CurrentDomain.FriendlyName + ": Thread 释放," + DateTime.Now);
            }
        }
    }
 
 
 
 
 
 
第一个例子说明,在同进程同域,不同线程下,锁type int,其实锁的是同一个int对象。所以要慎用。
 
第二个例子,这里就简单说下。
 
      A: CLR启动时,会创建 系统域(System Domain)和共享域(Shared Domain), 默认程序域(Default AppDomain)。 系统域和共享域是单例的。程序域可以有多个,例子中我们使用AppDomain.CreateDomain方法创建的。
 
      B:  按正常来说,每个程序域的代码都是隔离,互不影响的。但对于一些基础类型来说,每个程序域都重新加载一份,就显得有点浪费,带来额外的损耗压力。聪明的CLR会把一些基本类型Object, ValueType, Array, Enum, String, and Delegate等所在的程序集MSCorLib.dll,在CLR启动过程中都会加载到共享域。  每个程序域都会使用共享域的基础类型实例。  
 
      C: 而每个程序域都有属于自己的托管堆。托管堆中最重要的是GC heap和Loader heap。GC heap用于引用类型实例的存储,生命周期管理和垃圾回收。Loader heap保存类型系统,如MethodTable,数据结构等,Loader heap生命周期不受GC管理,跟程序域卸载有关。
 
     所以共享域中Loader heap MSCorLib.dll中的int实例会一直保留着,直到进程结束。单个程序域卸载也不受影响。作用域很大有没有!!!
 
     这时第二个例子也很容易理解了。 锁int实例是跨程序域的,MSCorLib中的基础类型都是这样。 极容易造成死锁,慎用。  而自定义类型则会加载到自己的程序域,不会影响别人。
 
3:字符串的锁
 
我们都知道锁的目的,是为了多线程下值被破坏。也知道string在c#是个特殊对象,值是不变的,每次变动都是一个新对象值,这也是推荐stringbuilder原因。如例:
 
 
//======Example 4=====
        string str1 = "mushroom";
        string str2 = "mushroom";
        var result1 = object.ReferenceEquals(str1, str2);
        var result2 = object.ReferenceEquals(str1, "mushroom");
        Console.WriteLine(result1 + "-" + result2);
        /* output
         * True-True
         */
  
 
 正式由于c#中字符串的这种特性,所以字符串是在多线程下是不会被修改的,只读的。它存在于SystemDomain域中managed heap中的一个hash table中。Key为string本身,Value为string对象的地址。
 
 当程序域需要一个string的时候,CLR首先在这个Hashtable根据这个string的hash code试着找对应的Item。如果成功找到,则直接把对应的引用返回,否则就在SystemDomain对应的managed heap中创建该 string,并加入到hash table中,并把引用返回。所以说字符串的生命周期是基于整个进程的,也是跨AppDomain。
 
4:monitor用法
 
介绍下Wait,Pulse,PulseAll的用法。有注释,大家直接看代码吧。
 
 
 static string str = "mushroom";
        static void Main(string[] args)
        {
            new Thread(() =>
            {
                bool isGetLock = false;
                Monitor.Enter(str, ref isGetLock);
                try
                {
                    Console.WriteLine("Thread1第一次获取锁");
                    Thread.Sleep(5000);
                    Console.WriteLine("Thread1暂时释放锁,并等待其他线程释放通知信号。");
                    Monitor.Wait(str); 
                    Console.WriteLine("Thread1接到通知,第二次获取锁。");
                    Thread.Sleep(1000);
                } 
                finally
                {
                    if (isGetLock)
                    {
                        Monitor.Exit(str);
                        Console.WriteLine("Thread1释放锁");
                    }
                }
            }).Start();
            Thread.Sleep(1000);
            new Thread(() =>
            {
                bool isGetLock = false;
                Monitor.Enter(str, ref isGetLock); //一直等待中,直到其他释放。
                try
                {
                    Console.WriteLine("Thread2获得锁");
                    Thread.Sleep(5000);
                    Monitor.Pulse(str); //通知队列里一个线程,改变锁状态。  Pulseall 通知所有的
                    Console.WriteLine("Thread2通知其他线程,改变状态。");
                    Thread.Sleep(1000);
                }
                finally
                {
                    if (isGetLock)
                    {
                        Monitor.Exit(str);
                        Console.WriteLine("Thread2释放锁");
                    }
                }
 
            }).Start();
            Console.ReadLine();
 
 
二:mutex
 lock是不能跨进程锁的。 mutex作用和lock类似,是能跨进程锁的。 我们来看个例子
 
 
    static bool createNew = false;
        //第一个参数 是否应拥有互斥体的初始所属权。即createNew true时,mutex默认获得处理信号
        //第二个是名字,第三个是否成功。
        public static Mutex mutex = new Mutex(true, "mushroom.mutex", out createNew);
 
        static void Main(string[] args)
        {
            //======Example 5=====
            if (createNew)  //第一个创建成功,这时候已经拿到锁了。 无需再WaitOne了。一定要注意。
            {
                try
                {
                    Run();
                }
                finally
                {
                    mutex.ReleaseMutex(); //释放当前锁。  
                }
            }
            //WaitOne 函数作用是阻止当前线程,直到拿到收到其他实例释放的处理信号。
            //第一个参数是等待超时时间,第二个是否退出上下文同步域。
            else if (mutex.WaitOne(10000,false))//
            {
                try
                {
                    Run();
                }
                finally
                {
                    mutex.ReleaseMutex();
                }
            }
            else//如果没有发现处理信号
            {
                Console.WriteLine("已经有实例了。");
                Console.ReadLine();
            }
        }
        static void Run()
        {
            Console.WriteLine("实例1");
            Console.ReadLine();
        }
 
 
 
我们顺序起A  B实例测试下。   A首先拿到锁,输出 实例1 。   B在等待, 如果10秒内A释放,B拿到执行Run()。  超时后输出  已经有实例了。
 
这里注意的是第一个拿到处理信号 的实例,已经拿到锁了。不需要再WaitOne。  否则报异常。  
 
 
 
三:Semaphore
 即信号量,我们可以把它理解为升级版的mutex。mutex对一个资源进行锁,semaphore则是对多个资源进行加锁。
 
semaphore内部一个线程计数器,线程每调用一次,计数器减一,释放后对应加一。 超出线程数量则排队等候。semaphore也是可以跨进程的。
 
 
 static void Main(string[] args)
        {
            Console.WriteLine("准备处理队列");
 
            bool createNew = false;
 
            SemaphoreSecurity ss = new SemaphoreSecurity(); //信号量权限控制
            Semaphore semaphore = new Semaphore(2, 2, "mushroom.Semaphore", out createNew,null);
            for (int i = 1; i <= 5; i++)
            {
                new Thread((arg) =>
                {
                    semaphore.WaitOne();
                    Console.WriteLine(arg + "处理中");
                    Thread.Sleep(10000);
                    semaphore.Release(); //即semaphore.Release(1)
                    //semaphore.Release(5);可以释放多个,但不能超过最大值。如果最后释放的总量超过本身总量,也会报错。 不建议使用
 
                }).Start(i);
            }
            Console.ReadLine();
        }
 
 
 
四:总结
 mutex和Semaphore  性能较差,需要跨进程的时候,再使用。
 
 lock和Monitor    性能较好些。
 
 注意死锁。

c#语言-多线程中的锁系统(一)的更多相关文章

  1. 多线程中的锁系统(二)-volatile、Interlocked、ReaderWriterLockSlim

    上章主要讲排他锁的直接使用方式.但实际当中全部都用锁又太浪费了,或者排他锁粒度太大了,本篇主要介绍下升级锁和原子操作. 阅读目录 volatile Interlocked ReaderWriterLo ...

  2. 多线程中的锁系统(三)-WaitHandle、AutoResetEvent、ManualResetEvent

    本章主要介绍下基于内核模式构造的线程同步方式,事件,信号量. 阅读目录: 理论 WaitHandle AutoResetEvent ManualResetEvent 总结 理论 Windows的线程同 ...

  3. C#多线程编程中的锁系统

    C#多线程编程中的锁系统(二) 上章主要讲排他锁的直接使用方式.但实际当中全部都用锁又太浪费了,或者排他锁粒度太大了. 这一次我们说说升级锁和原子操作. 目录 1:volatile 2:  Inter ...

  4. java 多线程中的锁的类别及使用

    目前在Java中存在两种锁机制: synchronized Lock Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea. 数据同步需要依赖锁,那锁的同步又依赖谁? ...

  5. java 多线程研究:锁的概念

    java多线程:锁 java的多线程中的锁是干嘛的呢?在网上找了很多博客,大都是很专业的语言,让我一时间摸不着头脑.下面分三个部分来总结多线程中的锁的概念. 一,基础概念: 多线程在运行的时候可能会遇 ...

  6. 转载~kxcfzyk:Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解

    Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解   多线程c语言linuxsemaphore条件变量 (本文的读者定位是了解Pthread常用多线程API和Pthread互斥锁 ...

  7. 【多线程】不懂什么是 Java 中的锁?看看这篇你就明白了!

    本文来源:Java建设者 原文地址:https://mp.weixin.qq.com/s/GU42BjM5jY2CEMVD_PAZBQ Java 锁分类 Java 中的锁有很多,可以按照不同的功能.种 ...

  8. iOS开发中多线程间关于锁的使用

    为什么需要使用锁,当然熟悉多线程的你,自然不会感到陌生. 那你在代码中是否很好的使用了锁的机制呢?你又知道几种实现锁的方法呢? main.m 1 int main(int argc, const ch ...

  9. Java多线程并发08——锁在Java中的应用

    前两篇文章中,为各位带来了,锁的类型及锁在Java中的实现.接下来本文将为各位带来锁在Java中的应用相关知识.关注我的公众号「Java面典」了解更多 Java 相关知识点. 锁在Java中主要应用还 ...

随机推荐

  1. java多线程 join方法以及优先级方法

    /*join:当A线程执行到了B线程的.join()方法时,A就会等待.等B线程都执行完,A才会执行. join可以用来临时加入线程执行. 1.线程使用join方法,主线程就停下,等它执行完,那么如果 ...

  2. PHPEXCEL使用实例

    最近在项目中要用到PHP生成EXCEL,上网找了一下,发现PHPEXCEL挺不错,用了一下,感觉还行,就是设置单元格格式的时候比较麻烦,总体来说功能还是比较强大的,还有生成PDF什么的,发一个实例吧 ...

  3. Poj 1503 Integer Inquiry

    1.链接地址: http://poj.org/problem?id=1503 2.题目: Integer Inquiry Time Limit: 1000MS   Memory Limit: 1000 ...

  4. 模板:LCS(最长公共子序列)

    #include <cstring> #define max(a,b) ((a) > (b) ? (a) : (b)) int same(char ch1,char ch2) { ; ...

  5. NSInteger 与 NSUInteger 和 int与 NSInteger 区别(转)

    转自:http://blog.csdn.net/duxinfeng2010/article/details/7606261 先说说NSInteger 与 NSUInteger,在看书上代码是遇见NSI ...

  6. 省市选择(基于zepto.js)

    效果如下: <div class="clList overflow-h mt75"> <select class="pull-left cl-35 se ...

  7. 几个css的小知识点(一)

    1.对于不能确定宽度的div让它水平居中. <div class='father'> <div class='son'>居中</div> </div> ...

  8. Sublime Text3快捷键一览表

    选择类 Ctrl+D 选中光标所占的文本,继续操作则会选中下一个相同的文本. Alt+F3 选中文本按下快捷键,即可一次性选择全部的相同文本进行同时编辑.举个栗子:快速选中并更改所有相同的变量名.函数 ...

  9. 第一部分实现功能:使用一个TabControl和一个Memo和TDictionary类实现文本临时存储

    效果图: 一期功能概要: a.双击tab关闭tab,双击tab右边空白添加tab(标题为以hhnnsszzz的时间格式命名) b.切换tab将数据存入dictionary,key为标题,value为m ...

  10. 捕获ClientDataSet.ApplyUpdates和SocketConnection异常

    核心提示:如何捕获ClientDataSet.ApplyUpdates的错误,不用ReconcileError... var cdsEmp:TClientDataSet; //保存 procedure ...