线程通信(一)—— 互斥锁

在使用线程时,经常要注意的就是访问临界资源加锁。

在编码过程由于粗心忘记加锁将带来不可预知的错误。这类错误单次运行或小并发时难以复现,当数据量变大,用户数增多时,轻则系统崩溃,大则引起数据错误。造成损失。

线程中互斥锁与进程的信号量类似,也可以看做是PV操作,用于保护临界资源,确保只有一个线程访问。

下面代码是不加锁错误代码,其中也涉及到之前提到的线程编程时需要注意的一些小细节。

  1 #include <pthread.h>
  2 #include <unistd.h>
  3 #include <iostream>
  4
  5 using namespace std;
  6
  7 class ThreadInterface
  8 {
  9 public:
 10     void CreateThread(void* (*func)(void *));
 11     void WaitThread();
 12 private:
 13     pthread_t m_pTread;
 14 };
 15
 16 void ThreadInterface::CreateThread(void* (*func)(void *))
 17 {
 18     pthread_create(&m_pTread, NULL, func, NULL);
 19 }
 20
 21 void ThreadInterface::WaitThread()
 22 {
 23     pthread_join(m_pTread, NULL);
 24 }
 25
 26 class MutexLockInterface
 27 {
 28 public:
 29     void CreateMutexLock();
 30     void GetMutexLock();
 31     void ReleaseMutexLock();
 32     void DestroyMutexLock();
 33 private:
 34     pthread_mutex_t m_MutexLock;
 35 };
 36
 37 void MutexLockInterface::CreateMutexLock()
 38 {
 39     int ret = pthread_mutex_init(&m_MutexLock, NULL);
 40     if (0 != ret)
 41         cout<<"init mutex error!";
 42 }
 43
 44 void MutexLockInterface::GetMutexLock()
 45 {
 46     pthread_mutex_lock(&m_MutexLock);
 47 }
 48
 49 void MutexLockInterface::ReleaseMutexLock()
 50 {
 51     pthread_mutex_unlock(&m_MutexLock);
 52 }
 53
 54 void MutexLockInterface::DestroyMutexLock()
 55 {
 56     pthread_mutex_destroy(&m_MutexLock);
 57 }
 58
 59
 60 class Service
 61 {
 62 public:
 63     static void* run(void *)    //类成员线程函数为static去除this指针
 64     {
 65         //m_MutexLock.GetMutexLock();
 66         if (0 == m_Tickets)
 67         {
 68             cout<<"stop operate!"<<endl;
 69         }
 70         else
 71         {
 72             cout<<"window2:we have "<<m_Tickets<<"Tickets"<<endl;
 73             sleep(1);
 74             --m_Tickets;
 75         }
 76         //m_MutexLock.ReleaseMutexLock();
 77     }
 78     int SetData(int data){m_Tickets = data;};
 79     int GetData(){return m_Tickets;};
 80     static int m_Tickets;
 81     static MutexLockInterface m_MutexLock;
 82 };
 83 int Service::m_Tickets = 1; //静态变量类外初始化
 84 MutexLockInterface Service::m_MutexLock;
 85
 86 int main()
 87 {
 88     Service Srv;
 89     ThreadInterface Thread;
 90     Srv.m_MutexLock.CreateMutexLock();
 91
 92     Thread.CreateThread(&Srv.run);
 93
 94     //Srv.m_MutexLock.GetMutexLock();
 95     if (0 == Srv.GetData())
 96     {
 97         cout<<"stop operate!"<<endl;
 98     }
 99     else
100     {
101         cout<<"window1:we have "<<Srv.GetData()<<"Tickets"<<endl;
102         sleep(1);  //延时1s等待线程2
103         Srv.SetData(Srv.GetData() - 1);
104     }
105     //Srv.m_MutexLock.ReleaseMutexLock();
106     Thread.WaitThread();    //等待线程结束回收
107     cout<<Srv.GetData()<<endl;
108     return 0;
109 }

上述代码以售票为场景,当票只剩下一张时,两个窗口同时有人需要购票。

线程不加锁,执行结果如下:

很显然这不是我们想要的结果,只有一张票却卖出去了两张,最后余票显示为-1!

去除注释行,对临界资源操作是加锁,再运行程序,得到与预期一致的结果!

这就是线程互斥锁存在的原因。

时间: 2024-11-05 13:33:18

线程通信(一)—— 互斥锁的相关文章

APUE学习笔记——11 线程同步、互斥锁、自旋锁、条件变量

线程同步 同属于一个进程的不同线程是共享内存的,因而在执行过程中需要考虑数据的一致性. 假设:进程有一变量i=0,线程A执行i++,线程B执行i++,那么最终i的取值是多少呢?似乎一定是i=2:其实不然,如果没有考虑线程同步,i的取值可能是1.我们先考虑自加操作的过程:a,首先将内存中i的值copy到寄存器:b,对寄存器中i的copy进行自加:c,将寄存器中自加的结果返回到内存中.回到例子,如果线程A执行完abc三个步骤,线程B在执行者三个步骤,那么结果就应该为2.但是自加不是原子操作,假如执行

UNIX网络编程卷1 服务器程序设计范式7 预先创建线程,以互斥锁上锁方式保护accept

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.预先创建一个线程池,并让每个线程各自调用 accept 2.用互斥锁取代让每个线程都阻塞在 accept 调用之中的做法 //用于维护关于每个线程基于信息的 Thread 结构 typedef struct { pthread_t thread_tid; /* 线程 ID */ long thread_count; /* 处理的连接数 */ } Thread; Thread *tptr

多线程同步互斥实例——使用synchronized实现线程通信和互斥

线程互斥概念 线程互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性.但互斥无法限制访问者对资源的访问顺序,即访问是无序的. 实现线程同步互斥的四种方式 临界区(Critical Section):适合一个进程内的多线程访问公共区域或代码段时使用 互斥量 (Mutex):适合不同进程内多线程访问公共区域或代码段时使用,与临界区相似. 事件(Event):通过线程间触发事件实现同步互斥 信号量(Semaphore):与临界区和互斥量不同,可以实现多个线程同时访问公共区域数据,原理

ucos实时操作系统学习笔记——任务间通信(互斥锁)

想讲一下ucos任务间通信中的mutex,感觉其设计挺巧妙,同sem一样使用的是event机制实现的,代码不每一行都分析,因为讲的没邵贝贝老师清楚,主要讲一下mutex的内核是如何实现的.可以理解互斥锁是设置信号量值为1时候的特殊情况,与之不同的地方是互斥锁为了避免优先级反转采用了优先级继承机制,本文主要讲一下互斥锁的创建,pend和post,对应的函数是OSMutexCreate,OSMutexPend,OSMutexPost,当然讲函数也不会所有的扩展功能都讲,只是讲一下主干部分,下面贴出来

线程同步(互斥锁与信号量的作用与区别)

“信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在 哪里).而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这 个资源.比如对全局变量的访问,有时要加锁,操作完了,在解锁.有的时候锁和信号量会同时使用的” 也就是说,信号量不一定是锁定某一个资源,而是流程上的概念,比如:有A,B两个线程,B线程要等A线程完成某一任务以后

C11线程管理:互斥锁

1.概述 锁类型 c11提供了跨平台的线程同步手段,用来保护多线程同时访问的共享数据. std::mutex,最基本的 Mutex 类,独占的互斥量,不能递归使用. std::time_mutex,带超时的独占互斥量,不能递归使用. std::recursive_mutex,递归互斥量,不带超时功能. std::recursive_timed_mutex,带超时的递归互斥量. lock类型 std::lock_guard,与 Mutex RAII 相关,方便线程对互斥量上锁. std::uniq

线程&mdash;同步之互斥锁

线程互斥:实质某一资源同时只允许一个访问者对其进行访问,具有唯一性和排他性. import threading import time g_lock = threading.Lock() def func(): global tmp global g_lock g_lock.acquire() for i in range(20,40): tmp = i] print "%s:tmp = %d"%(threading.currentThread(),getName(),tmp) tim

线程同步机制之互斥锁

进程间通讯介绍 1.几种进程间的通信方式 # 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用.进程的亲缘关系通常是指父子进程关系. # 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信. # 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问.它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源.因此,主要作为进程间以及同一进

第9章 线程编程(4)_线程同步1:互斥锁

5. 线程的互斥和同步 5.1 同步和互斥的概念 (1)线程同步:是一个宏观概念,在微观上包含线程的相互排斥和线程的先后执行的约束问题.解决同步方式一般采用条件变量和信号量. (2)线程互斥:线程执行的相互排斥(注意,它不关心线程间执行的先后顺序!).解决互斥一般使用互斥锁.读写锁和信号量. [编程实验]银行ATM(线程不安全的例子) //account.h #ifndef __ACCOUNT_H__ #define __ACCOUNT_H__ typedef struct { int code

Windows线程同步【3】互斥锁(Mutex)

我们前面讲过的临界区,如同一个小房间,张三进去了,李四就不能进,如果李四要进,必须等张三出来. 今天我们要讲的互斥锁,像一个物件,这个物件只能同时被一个线程持有.如此一来,便可以通过互斥锁来实现线程的同步. 一.创建 创建互斥锁的方法是调用函数CreateMutex: CreateMutex(&sa, bInitialOwner, szName); 第一个参数是一个指向SECURITY_ATTRIBUTES结构体的指针,一般的情况下,可以是nullptr. 第二个参数类型为BOOL,表示互斥锁创