菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
34
0

线程基础篇-线程同步

原创
05/13 14:22
阅读数 50140

Lock是将一段代码定义为临界区,临界区中的代码在同一时刻只能有一个线程访问,

当临界区代码执行时,其他线程调用会被阻塞,需等待资源释放。

语法:

private object locker=new object();

void Method()

{

       lock(locker)

       {

              代码块…

}

}

注意:

1 lock不能锁定空值

2 lock不能string类型,因为字符驻留机制,无法被释放

3 lock不能锁定值类型

4 lock避免锁定public类型或不受程序控制的对象,容易出现死锁

实例

 基础类

 1  class CTest
 2     {
 3         private bool deadlocked = true;
 4         private object locker = new object();
 5         public void LockMe(object o)
 6         {
 7             lock(locker)
 8             {
 9                 while(deadlocked)
10                 {
11                     deadlocked=(bool)o;
12                     Console.WriteLine("Foo:I am locked :(");
13                     Thread.Sleep(500);
14                 }
15             }
16         }
17         public void DoNotLockMe()
18         {
19             Console.WriteLine("I am not locked:)");
20         }
21     }
View Code

操作

 1 static void Main(string[] args)
 2         {
 3             CTest c1 = new CTest();
 4             Thread th = new Thread(c1.LockMe);
 5             th.Start(true);
 6             Thread.Sleep(100);
 7             lock (c1)
 8             {
 9                 c1.DoNotLockMe();
10                 c1.LockMe(false);
11             }
12             Console.ReadKey();
13         }
View Code

显示效果:

 

Monitor

lcok的底层是Monitor.Enter和Moniter.Exit,有了lock语法糖可以轻松实现加锁操作,为了更精确的操作,需要使用Monitor类

Monitor.Enter 上锁,锁定资源

Monitor.TryEnter 在指定超时时间内锁定资源,可避免死锁

Monitor.Wait  暂时释放资源

Monitor.Pulse  唤醒等待队列中的线程

Monitor.Exit  释放资源

实例:

 

打印奇偶数方法

 1       public void PrintEven()
 2         {
 3             Monitor.Enter(this);
 4             try
 5             {
 6                 for (int i = 0; i <= 10; i = i + 2)
 7                 {
 8                     Console.WriteLine(Thread.CurrentThread.Name + "------" + i);
 9                 }
10             }
11             catch (Exception)
12             {
13 
14                 throw;
15             }
16             finally
17             {
18                 Monitor.Exit(this);
19             }
20         }
21 
22         public void PrintOdd()
23         {
24             Monitor.Enter(this);
25             try
26             {
27                 for (int i = 1; i <= 10; i = i + 2)
28                 {
29                     Console.WriteLine(Thread.CurrentThread.Name + "------" + i);
30                 }
31             }
32             catch (Exception)
33             {
34 
35                 throw;
36             }
37             finally
38             {
39                 Monitor.Exit(this);
40             }
41 
42         }
View Code

操作

 1       static void Main(string[] args)
 2         {
 3 
 4             Program p = new Program();
 5             Thread the = new Thread(p.PrintEven);
 6             the.IsBackground = true;
 7             the.Name = "打印偶数";
 8             the.Start();
 9             Thread tho = new Thread(p.PrintOdd);
10             tho.IsBackground = true;
11             tho.Name = "打印奇数";
12             tho.Start();
13          
14             Console.ReadKey();
15         }
View Code

 显示效果:

ReaderWriterLock

主要解决类似数据库这种读取数据多写入数据少的情况,如用Monitor类则形成独占,不能实现多个线程读取数据,读写锁很好的解决了这一情况

主要包括以下几个方法:

AcquireReaderLock 获取读取锁

ReleaseReaderLock 释放读取锁

AcquireWriterLock 获取写入锁

ReleaseWriterLock 释放写入锁

UpgradeToWriterLock 读锁转换为写锁

DowngradeFromWriterLock 写锁转换为读锁

 

Mutex

跨多个线程同步访问的类,只有一个线程能获得互斥锁定,访问受互斥保护的同步代码区域。

WaitOne 等待资源释放

ReleaseMutex 释放资源

 

最常用的是程序启动时判断是否已有实例在运行

代码如下:

 1        static void Main()
 2         {
 3             bool flag = false;
 4             bool requestInitialOwnership = true;
 5             Mutex mt = new Mutex(requestInitialOwnership, "MutexTest1", out flag);
 6             if (flag)
 7             {
 8                 Application.EnableVisualStyles();
 9                 Application.SetCompatibleTextRenderingDefault(false);
10                 Application.Run(new Form1());
11                 
12             }
13             else
14             {
15                 MessageBox.Show("MutexTest1已启动");
16                 Application.Exit();
17             }
18             
19         }
View Code

显示效果:

排队买票,同一窗口在同一时间只能有一个人买票

 买票方法

 1         private static Mutex mt = new Mutex();
 2         public static void buyTichets(Object name)
 3         {
 4             if (mt.WaitOne())
 5             {
 6                 try
 7                 {
 8                     Console.WriteLine("{0}占用窗口并开始买票",name);
 9                     Thread.Sleep(1000);
10                 }
11                 catch (Exception)
12                 {
13 
14                     throw;
15                 }
16                 finally
17                 {
18                     Console.WriteLine("{0}离开窗口",name);
19                     mt.ReleaseMutex();
20                 }
21             }
22         }  
View Code

操作

1       static void Main(string[] args)
2         {
3             Thread th1 = new Thread(buyTichets);
4             th1.Start("小明");
5             Thread th2 = new Thread(buyTichets);
6             th2.Start("小A");
7             Console.ReadKey();
8         }
View Code

显示效果:

 

Interlocked 

为多线程共享整数变量提供原子操作,类似操作系统的PV操作

Interlocked.Read 读取计数器的值

Interlocked.Add 使计数器增加指定的值

Interlocked.Increment 使计数器加一

Interlocked.Decrement 使计数器减一

Interlocked.Exchange  把计数器设定为某个指定值

Interlocked.CompareExchange 将计数器与某个值比较,若相等则计数器设定为指定的值

实例

 1         private static char buffer;
 2         private static long used = 0;
 3         static void Main(string[] args)
 4         {
 5             string str = "壬戌之秋,七月既望,苏子与客泛舟游于赤壁之下。清风徐来,水波不兴。举酒属客,诵明月之诗,歌窈窕之章。";
 6             Thread thWriter=new Thread(delegate()
 7             {
 8                 for (int i = 0; i < str.Length; i++)
 9                 {
10                     while (Interlocked.Read(ref used) == 1)
11                     {
12                         Thread.Sleep(50);
13                     }
14                     buffer = str[i];
15                     Interlocked.Increment(ref used);
16                 }
17                
18             });
19             Thread thReader = new Thread(delegate ()
20               {
21                   for (int i = 0; i < str.Length; i++)
22                   {
23                       while (Interlocked.Read(ref used) == 0)
24                       {
25                           Thread.Sleep(50);
26                       }
27                       char ch = buffer;
28                       Console.Write(ch);
29                       Interlocked.Decrement(ref used);
30                   }
31               });
32             thWriter.Start();
33             thReader.Start();
34             Console.ReadKey();
35         }
View Code

显示效果:文字会一个一个的打印出来,并且不会乱。

  

AutoResetEvent ManualResetEvent

实现线程通信,类似信号量Semaphore

Set()  将信号状态设置为有信号

Reset() 将信号状态设置为无信号

WaitOne()  无信号时线程阻塞,有信号时线程无阻塞

AutoResetEvent和ManualResetEvent的区别在于WaitOne方法,AutoResetEvent的WaitOne会自动改变事件对象的状态,每次只能唤醒一个线程。

 

发表评论

0/200
34 点赞
0 评论
收藏
为你推荐 换一批