`
hulunberbus
  • 浏览: 896231 次
文章分类
社区版块
存档分类
最新评论

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             //注意,这里使用的是同一个资源对象obj
15             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 Jack
35     {
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 John
66     {
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 }
复制代码

0
0
分享到:
评论

相关推荐

    多线程编程——线程的同步

    在“多线程编程之四——线程的同步”这个文件中,可能包含了上述各种同步机制的具体实现示例和详细说明,这对于初学者来说是一份非常宝贵的参考资料。通过学习和理解这些例子,开发者可以更好地掌握如何在实际项目中...

    c++多线程同步——信号量

    总之,理解并正确使用C++中的信号量机制对于编写高效、可靠的多线程程序至关重要。在MFC工程中,通过自定义信号量类和Windows API,我们可以有效地解决多线程同步问题,确保程序的正确性和性能。

    Visual C++高级编程技术——MFC与多线程篇.rar

    本教程“Visual C++高级编程技术——MFC与多线程篇”将深入探讨这两个关键概念。 MFC是C++面向对象编程的一个重要框架,它基于Windows API,将复杂的Win32 API函数封装为易于理解和使用的类。MFC包含了一系列的类,...

    多线程编程之三——线程间通讯

    标题与描述均提到了“多线程编程之三——线程间通讯”,这明确指出了文章的核心主题:在多线程编程环境下,不同线程之间的通信机制。在现代软件开发中,尤其是涉及到高性能计算、并发处理以及分布式系统设计时,线程...

    12.1 Qt5多线程:多线程及简单实例

    本知识点将深入探讨Qt5中的多线程以及一个简单的实例——WorkThread。 **1. 多线程概念** 多线程是指在一个进程中同时执行多个独立的代码段,每个代码段称为线程。这种并行处理可以提高程序的执行效率,尤其是在...

    pb9多线程控件,能够真实实现多线程

    1. PowerBuilder 9.0的多线程实现:PB9不直接支持多线程,但可以通过第三方控件或自定义编程实现。 2. Ttimer.ocx控件:这是一个可能用于多线程环境的ActiveX定时器控件,可以触发并发操作。 3. 多线程的优势:多...

    C#多线程读写sqlite

    5. **性能测试**:为了评估多线程读写SQLite的性能,通常会进行计时测试。这可以通过`Stopwatch`类来实现,它可以精确地测量代码执行的时间,帮助优化并发策略。 **第一演示项目(firstdemo)**: 这个项目的源代码...

    delphi7 多线程测试(40个线程)

    3. **资源竞争**:当多个线程访问相同的资源,如内存、磁盘I/O或数据库连接时,可能会出现竞态条件,需要通过锁或其他同步机制来解决。这会导致线程等待,增加了整体执行时间。 4. **工作负载平衡**:如果40个线程...

    c++多线程编程的十个例子

    在C++编程中,多线程技术是一种强大的工具,它允许程序同时执行多个任务,从而提高了效率和响应性。以下是对“C++多线程编程的十个例子”的详细讲解,这些例子将帮助你在Windows环境下深入理解和应用多线程。 1. **...

    MFC多线程编程实例----多线程画线源码

    在标题中提到的“MFC多线程编程实例——多线程画线源码”,我们主要关注的是如何在同一个窗口或图形界面上,通过多个线程同时执行画线操作。这通常涉及到以下几个关键知识点: 1. **线程基础**:在计算机科学中,...

    C#多线程排序例子

    为了防止数据竞争,可能需要使用锁(如`Monitor`类,`Mutex`,或`Semaphore`)进行线程同步,确保在多线程环境中正确地访问共享资源。 3. **线程优先级**:C#允许为线程设置优先级,如`ThreadPriority.Lowest`、`...

    Linux c++多线程串口编程demo

    3. **互斥锁(Mutex)**:在多线程环境中,互斥锁用于保护共享资源,确保同一时间只有一个线程可以访问。`std::mutex`类提供了互斥锁的功能,可以防止数据竞争,确保数据的一致性和完整性。在这个demo中,可能使用...

    server_c_Linux.rar_linux 多线程_多线程 服务器_多线程通信

    2. **线程同步**:由于多线程环境下可能会存在数据竞争,因此需要使用`pthread_mutex_t`互斥锁或`pthread_rwlock_t`读写锁来保护共享资源的安全性,确保同一时刻只有一个线程访问。 3. **套接字编程**:服务器会...

    一个多线程示例程序及多线程常见问题介绍

    在IT领域,多线程是程序设计中的一个重要概念,它允许程序同时执行多个任务,显著提高了计算机系统的效率和响应速度。C++Builder是一款强大的集成开发环境(IDE),它支持C++语言,为开发者提供了创建多线程应用的...

    TensorRT python多进程推理踩坑(csdn)————程序.pdf

    在使用TensorRT进行深度学习推理时,特别是在Python环境中,可能会遇到多线程或多进程的优化问题。本篇文章主要探讨了在TensorRT中实现多线程推理以及如何在Python的multiprocessing库下正确运行多进程推理的注意...

    Qt5之TCP多线程收发

    在Qt5中,TCP多线程收发是网络编程中的一个重要话题,特别是在开发实时性要求高、并发量大的应用程序时。TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,而多线程技术则可以有效利用...

    QT/C++ TCP多线程客户端(收发线程分离,自动粘包处理,自动数据包成型)

    1. **多线程模型**:在本项目中,采用了两种线程来处理不同的任务——一个用于接收数据,另一个用于发送数据。这种设计可以避免因接收和发送操作相互阻塞而降低效率。接收线程专注于从服务器持续读取数据,而发送...

    c++多线程源码

    C++多线程是现代C++编程中一个重要的特性,它允许程序同时执行多个任务,以提高程序的并发性和效率。在C++11及更高版本中,标准库提供了对多线程的支持,使得开发者可以方便地创建和管理线程。 在C++中,创建线程...

    《软件开发基础(Java)》实验报告-Java多线程编程.docx

    实验中,使用了经典的分治策略——归并排序,将大数组分成多个小数组,每个小数组由单独的线程进行排序。具体步骤如下: 1. 初始化一维数组,填充随机数据。 2. 将数组分成多个子数组,每个子数组对应一个线程。 3. ...

    Qt 串口,多线程(子线程处理串口信号)

    **Qt 串口与多线程技术** 在嵌入式开发和桌面应用中,Qt库是一种广泛使用的跨平台开发框架,它提供了丰富的功能,包括GUI设计、网络通信、数据库访问等。在标题“Qt 串口,多线程(子线程处理串口信号)”中,我们...

Global site tag (gtag.js) - Google Analytics