【转载】5天不再惧怕多线程——第二天 锁机制

当多个线程在并发的时候,难免会碰到相互冲突的事情,比如最经典的ATM机的问题,并发不可怕,可怕的是我们没有能力控制。

线程以我的理解可以分为三种

① 锁。

② 互斥。

③ 信号。

好,这一篇主要整理“锁”,C#提供了2种手工控制的锁

一:  Monitor类

这个算是实现锁机制的纯正类,在锁定的临界区中只允许让一个线程访问,其他线程排队等待。主要整理为2组方法。

1:Monitor.Enter和Monitor.Exit

微软很照护我们,给了我们语法糖Lock,对的,语言糖确实减少了我们不必要的劳动并且让代码更可观,但是如果我们要精细的

控制,则必须使用原生类,这里要注意一个问题就是“锁住什么”的问题,一般情况下我们锁住的都是静态对象,我们知道静态对象

属于类级别,当有很多线程共同访问的时候,那个静态对象对多个线程来说是一个,不像实例字段会被认为是多个。

不加锁的情况:

 1    class Program 2     { 3         static void Main(string[] args) 4         { 5             for (int i = 0; i < 10; i++) 6             { 7                 Thread t = new Thread(Run); 8  9                 t.Start();10             }11         }12 13         //资源14         static object obj = new object();15 16         static int count = 0;17 18         static void Run()19         {20             Thread.Sleep(10);21 22             Console.WriteLine("当前数字:{0}", ++count);23         }24     }

加锁的情况:

 1     class Program 2     { 3         static void Main(string[] args) 4         { 5             for (int i = 0; i < 10; i++) 6             { 7                 Thread t = new Thread(Run); 8  9                 t.Start();10             }11         }12 13         //资源14         static object obj = new object();15 16         static int count = 0;17 18         static void Run()19         {20             Thread.Sleep(10);21 22             //进入临界区23             Monitor.Enter(obj);24 25             Console.WriteLine("当前数字:{0}", ++count);26 27             //退出临界区28             Monitor.Exit(obj);29         }30     }

2:Monitor.Wait和Monitor.Pulse

首先这两个方法是成对出现,通常使用在Enter,Exit之间。

Wait: 暂时的释放资源锁,然后该线程进入”等待队列“中,那么自然别的线程就能获取到资源锁。

Pulse:  唤醒“等待队列”中的线程,那么当时被Wait的线程就重新获取到了锁。

这里我们是否注意到了两点:

①   可能A线程进入到临界区后,需要B线程做一些初始化操作,然后A线程继续干剩下的事情。

②   用上面的两个方法,我们可以实现线程间的彼此通信。

下面举个例子来模拟两个人的对话。

 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Threading; 5  6 namespace Test 7 { 8     public class Program 9     {10         public static void Main(string[] args)11         {12             LockObj obj = new LockObj();13 14             //注意,这里使用的是同一个资源对象obj15             Jack jack = new Jack(obj);16             John john = new John(obj);17 18             Thread t1 = new Thread(new ThreadStart(jack.Run));19             Thread t2 = new Thread(new ThreadStart(john.Run));20 21             t1.Start();22             t1.Name = "Jack";23 24             t2.Start();25             t2.Name = "John";26 27             Console.ReadLine();28         }29     }30 31     //锁定对象32     public class LockObj { }33 34     public class Jack35     {36         private LockObj obj;37 38         public Jack(LockObj obj)39         {40             this.obj = obj;41         }42 43         public void Run()44         {45             Monitor.Enter(this.obj);46 47             Console.WriteLine("{0}:我已进入茅厕。", Thread.CurrentThread.Name);48 49             Console.WriteLine("{0}:擦,太臭了,我还是撤!", Thread.CurrentThread.Name);50 51             //暂时的释放锁资源52             Monitor.Wait(this.obj);53 54             Console.WriteLine("{0}:兄弟说的对,我还是进去吧。", Thread.CurrentThread.Name);55 56             //唤醒等待队列中的线程57             Monitor.Pulse(this.obj);58 59             Console.WriteLine("{0}:拉完了,真舒服。", Thread.CurrentThread.Name);60 61             Monitor.Exit(this.obj);62         }63     }64 65     public class John66     {67         private LockObj obj;68 69         public John(LockObj obj)70         {71             this.obj = obj;72         }73 74         public void Run()75         {76             Monitor.Enter(this.obj);77 78             Console.WriteLine("{0}:直奔茅厕,兄弟,你还是进来吧,小心憋坏了!",79                                Thread.CurrentThread.Name);80 81             //唤醒等待队列中的线程82             Monitor.Pulse(this.obj);83 84             Console.WriteLine("{0}:哗啦啦....", Thread.CurrentThread.Name);85 86             //暂时的释放锁资源87             Monitor.Wait(this.obj);88 89             Console.WriteLine("{0}:拉完了,真舒服。", Thread.CurrentThread.Name);90 91             Monitor.Exit(this.obj);92         }93     }94 }

二:ReaderWriterLock类

先前也知道,Monitor实现的是在读写两种情况的临界区中只可以让一个线程访问,那么如果业务中存在”读取密集型“操作,就

好比数据库一样,读取的操作永远比写入的操作多。针对这种情况,我们使用Monitor的话很吃亏,不过没关系,ReadWriterLock

就很牛X,因为实现了”写入串行“,”读取并行“。

ReaderWriteLock中主要用3组方法:

<1>  AcquireWriterLock: 获取写入锁。

ReleaseWriterLock:释放写入锁。

<2>  AcquireReaderLock: 获取读锁。

ReleaseReaderLock:释放读锁。

<3>  UpgradeToWriterLock:将读锁转为写锁。

DowngradeFromWriterLock:将写锁还原为读锁。

下面就实现一个写操作,三个读操作,要知道这三个读操作是并发的。

 1 namespace Test 2 { 3     class Program 4     { 5         static List<int> list = new List<int>(); 6  7         static ReaderWriterLock rw = new System.Threading.ReaderWriterLock(); 8  9         static void Main(string[] args)10         {11             Thread t1 = new Thread(AutoAddFunc);12 13             Thread t2 = new Thread(AutoReadFunc);14 15             t1.Start();16 17             t2.Start();18 19             Console.Read();20         }21 22         /// <summary>23 /// 模拟3s插入一次24 /// </summary>25 /// <param name="num"></param>26         public static void AutoAddFunc()27         {28             //3000ms插入一次29             Timer timer1 = new Timer(new TimerCallback(Add), null, 0, 3000);30         }31 32         public static void AutoReadFunc()33         {34             //1000ms自动读取一次35             Timer timer1 = new Timer(new TimerCallback(Read), null, 0, 1000);36             Timer timer2 = new Timer(new TimerCallback(Read), null, 0, 1000);37             Timer timer3 = new Timer(new TimerCallback(Read), null, 0, 1000);38         }39 40         public static void Add(object obj)41         {42             var num = new Random().Next(0, 1000);43 44             //写锁45             rw.AcquireWriterLock(TimeSpan.FromSeconds(30));46 47             list.Add(num);48 49             Console.WriteLine("我是线程{0},我插入的数据是{1}。", Thread.CurrentThread.ManagedThreadId, num);50 51             //释放锁52             rw.ReleaseWriterLock();53         }54 55         public static void Read(object obj)56         {57             //读锁58             rw.AcquireReaderLock(TimeSpan.FromSeconds(30));59 60             Console.WriteLine("我是线程{0},我读取的集合为:{1}",61                               Thread.CurrentThread.ManagedThreadId, string.Join(",", list));62             //释放锁63             rw.ReleaseReaderLock();64         }65     }66 }

转载地址:http://www.cnblogs.com/huangxincheng/archive/2012/03/14/2397068.html

时间: 2024-11-10 16:22:05

【转载】5天不再惧怕多线程——第二天 锁机制的相关文章

【转载】5天不再惧怕多线程——第五天 线程池

说到多线程,不可不说线程池,C#中关于池的概念很多,今天来整理下ThreadPool的使用. 是的,如果你很懒,如果你的执行任务比较短,如果你不想对线程做更精细的控制,那么把这些繁琐的东西丢给线程池吧. 一:ThreadPool 好了,下面看看TheadPool下有哪些常用的方法. 1:GetMaxThreads,GetMinThreads 首先我们肯定好奇线程池到底给我们如何控制线程数,下面就具体的看一看. 1 class Program 2 { 3 static void Main(stri

【转载】5天不再惧怕多线程——第三天 互斥体

没想到我的前两篇文章还挺受欢迎的,谢谢大家,今天整理下Mutex的使用. 一:Mutex 首先看下MSDN对它的解释: 不错,出现了一个亮点,可用于"进程间同步",既然进程间都可以同步,那线程同步对它来说不是小菜一碟吗?好的,还是看下Mutex在 线程中发挥的神奇功效. 1: 线程间同步 Metux中提供了WatiOne和ReleaseMutex来确保只有一个线程来访问共享资源,是不是跟Monitor很类似,下面我还是举个简单的例子, 注意我并没有给Metux取名字. 1 class

【转载】5天不再惧怕多线程——第一天 尝试Thread

原本准备在mongodb之后写一个lucene.net系列,不过这几天用到多线程时才发现自己对多线程的了解少之又少,仅仅停留在lock上面, 故这几天看了下线程参考手册结合自己的心得整理一下放在博客上作为自己的学习笔记. 好了,我们知道"负载"是一个很时尚,很牛X的玩意,往大处说,网站需要负载,数据库需要负载.往小处说,线程也需要负载,面对海量的 用户请求,我们的单线程肯定扛不住,那么怎么办,一定要负载,所以说多线程是我们码农必须要熟练掌握的一门技术. 在framework中给我们提供

【转载】5天不再惧怕多线程——第四天 信号量

今天整理"信号量"的相关知识,其实想想也蛮有趣的,锁,互斥,信号量都可以实现线程同步,在framework里面主要有三种. <1>:ManualResetEvent <2>:AutoResetEvent <3>: Semaphore 好,下面就具体看看这些玩意的使用. 一:ManualResetEvent 该对象有两种信号量状态True和False,好奇的我们肯定想知道True和False有什么区别,稍后的例子见分晓,有三个方法值得学习一下. 1:W

java 多线程9 : synchronized锁机制 之 代码块锁

synchronized同步代码块 用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间.这种情况下可以尝试使用synchronized同步语句块来解决问题.看一下例子: 下面例子是优化后的例子 使用代码块锁,原先例子是方法锁,就是同步 必须要执行2个for  public class ThreadDomain18 { public void doLongTimeTask() throws Exception

java 多线程8 : synchronized锁机制 之 方法锁

脏读 一个常见的概念.在多线程中,难免会出现在多个线程中对同一个对象的实例变量或者全局静态变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实是被更改过的.注意这里 局部变量是不存在脏读的情况 多线程线程实例变量非线程安全 看一段代码: public class ThreadDomain13 { private int num = 0; public void addNum(String userName) { try { if ("

Java多线程,对锁机制的进一步分析

1 可重入锁 可重入锁,也叫递归锁.它有两层含义,第一,当一个线程在外层函数得到可重入锁后,能直接递归地调用该函数,第二,同一线程在外层函数获得可重入锁后,内层函数可以直接获取该锁对应其它代码的控制权.之前我们提到的synchronized和ReentrantLock都是可重入锁. 通过ReEnterSyncDemo.java,我们来演示下synchronized关键字的可重入性. 1 class SyncReEnter implements Runnable{ 2 public synchro

python 多线程锁机制

GIL(全局解释器锁) GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,是为了实现不同线程对共享资源访问的互斥,才引入了GIL 在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势 python对于计算密集型的任务开多线程的效率甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的. GIL原理图 计算密集型:结果肯定是100,因为每一次start结果就已经出来了,所以第二个线程肯

“全栈2019”Java多线程第二十五章:生产者与消费者线程详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多线程第二十五章:生产者与消费者线程详解 下一章 "全栈2019"Java多线程第二十六章:同步方法生产者与消费者线程 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"