C++基础学习之16 - 多线程与互斥锁

news/2024/5/18 23:22:16 标签: C++, 多线程, 互斥, 互斥锁, mutex

        熟悉Linux的童鞋都对多进程比较熟,Linux下的 fork也有很多教程介绍,但这不是我们本节的重点,我们在这里主要讲的是多线程

        相对于进程来讲,线程 是一个轻量级的概念,一个进程包含多个线程(至少1个)。

        线程有自己的堆栈和局部变量,但没有内存空间,而是共享进程的内存空间。

        这种共享内存机制 优点在于通过数据共享的快速访问,同样这种机制 也会导致 一个线程死掉的时候可能导致整个程序崩溃

        

        那么多线程之间如何 有效共享一段数据呢?


一. 临界区(Critical Section)

        临界区Windows下专用的一种同步机制,一次只允许一个线程使用的共享资源,如果已经有线程进入了临界区,则其它线程必须等待。临界区 类似一种原子操作

void thread_func()  
{
    // 进入临界区  
    EnterCriticalSection();  
    
    // do something

    // 离开临界区
    LeaveCriticalSection();  
} 

        临界区 通过代码段的方式来控制 内存或数据的共享,其实现较为简单。


二. 互斥锁(Mutex)

        互斥 与临界区类似,也是确保同一时刻只能由一个线程进行访问,其区别在于:

1. Mutex可以跨进程,临界区只能在同一进程中使用;

2. Mutex是内核对象(核心态操作),速度慢;临界区是非内核对象(用户态操作),速度快;

3. Mutex和临界区在Windows平台都下可用,Linux下只能用 Mutex。

        对比一下在Linux和Windows平台下的Mutex使用代码:

/** Linux*/
#include <pthread.h>
pthread_mutex_t m_mutex;
pthread_mutex_init(&m_Mutex, NULL);

int nRet = pthread_mutex_lock(&m_Mutex);
…… // do something
nRet = pthread_mutex_unlock(&m_Mutex);

pthread_mutex_destroy(&m_Mutex); // 销毁

/** Windows*/
#include "windows.h"
HANDLE m_Mutex = CreateMutex(NULL, FALSE, NULL);
if( WAIT_OBJECT_0 == WaitForSingleObject(m_Mutex, INFINITE) )
{
    …… // do something
    ReleaseMutex(m_Mutex);
}
CloseHandle(m_Mutex); // 销毁


三. 信号量(Semaphore)

        信号量 是Mutex的一种扩展形式,信号量可以看作是一个资源池,当值大于0时,使用者可以锁定并使用,当小于0时,必须等待有新的资源释放才能去访问。

        当设置信号量最大值为1时,信号量退化为Mutex。

/** Linux*/
#include <pthread.h>
#include <semaphore.h>

void *thread_func() // 线程函数
{
    for(int i=0;i<50;i++)
    {
        sem_wait(&sem); //V操作,信号量-1
        printf("num %d: Now i come!",i);
        sem_post(&sem); //P操作,信号量+1
        printf("num %d: Now i leave!",i);
    }
}

void main()
{
    sem_t sem;
    sem_init(&sem,0,5); // 信号量值为5 - 理解为保证每个时刻最多5个人进入房间

    pthread_t th[5];
    for(int i=0;i<5;i++)
        pthread_create(&th[i], NULL, thread_func, NULL);
    // 销毁 pthread_join(&th[i], NULL);

    sem_destroy(&sem); // 关闭unnamed信号量(对应named信号量用sem_close)
}

/** Windows*/
#include "windows.h"
HANDLE hSemp = CreateSemaphore(NULL, 2, 2, NULL); // 创建信号量对象  

WaitForSingleObject(hSemp, INFINITE); // V操作,进入信号量
ReleaseSemaphore(hSemp, 1, NULL); // P操作,释放信号量
CloseHandle(hSemp); // 关闭信号量对象

四. 事件(Event)

        事件 与临界区一样,也是 Windows下独有的一种概念,事件是内核对象,多用于线程间通信,可以跨进程同步,主要用到三个函数:CreateEvent,OpenEvent,SetEvent,ResetEvent。

        OpenEvent与CreateEvent(创建)类似,是打开一个有名事件,我们重点说明另外两个函数:

        SetEvent:每次触发后,必有一个或多个处于等待状态下的线程变成可调度状态;

        ResetEvent:事件重置,回到未触发状态(注:bManualReset可以设置触发后自动重置)。

#include <windows.h>

HANDLE g_Event = CreateEvent(NULL,false,false,NULL); // HANDLE CreateEvent(LPSECURITY_ATTRIBUTE SlpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName);
SetEvent(g_Event);

// 线程绑定的函数返回值和参数是确定的,而且一定要__stdcall
int __stdcall threadFun()
{
    WaitForSingleObject(g_Event, INFINITE); // 等待事件触发
    SetEvent(g_Event); // 设置事件为触发状态,后面的线程再用
    return 0;
}

int main()
{
    HANDLE th1 = (HANDLE)_beginthreadex(NULL, 0, threadFun, NULL, 0, NULL); // _beginthreadex可以调用__stdcall
    HANDLE th2 = (HANDLE)_beginthreadex(NULL, 0, threadFun, NULL, 0, NULL); // 与CreateThread不同

    WaitForSingleObject(hth1, INFINITE); // - key point 
    WaitForSingleObject(hth2, INFINITE);

    CloseHandle(g_Event); // 关闭事件
    return 0;
}
        这样,整个逻辑变成,哪个线程触发事件,就可以执行自己的代码段,而其他线程必须等待该事件释放,从而达到同步的效果。

http://www.niftyadmin.cn/n/904862.html

相关文章

设计模式之1 - 抽象工厂Abstract Factory

工厂模式整体上是一个系列&#xff0c;建议 大家先看完后面的工厂方法模式&#xff0c;再回过头来看本章。 抽象工厂模式 是一种 创建类模式&#xff0c;和Factory Method类似&#xff0c;我们不去解释这个概念&#xff0c;网上能搜到的解释都很绕&#xff0c;越看越茫然&#…

Spring AOP Capabilities and Goals

Spring AOP是用纯Java实现的。不需要特殊的编译过程。Spring AOP不需要控制类加载器层次结构&#xff0c;因此适用于J2EE Web容器或应用程序服务器。 Spring AOP目前仅支持方法执行连接点&#xff08;建议在Spring bean上执行方法&#xff09;。虽然可以在不破坏核心Spring AOP…

设计模式之2 - 建造模式Builder

建造者模式 源自于复杂对象的构造过程&#xff0c;比如我们要建造一座房子&#xff0c;或者建造一辆车&#xff0c;Builder模式包含的角色包括&#xff1a; Director&#xff08;Designer&#xff09;&#xff1a;指导者&#xff08;或者 设计者&#xff09; Builder&#xff1…

设计模式之3 - 工厂方法Factory Method

工厂方法模式 又称为多态工厂模式&#xff0c;注意这里面的多态&#xff0c;意义在于定义一个创建产品对象的接口&#xff0c;基类本身不去创建对象&#xff0c;而是交给其子类或者是其实现类去创建。 通过上图可以看到&#xff0c;IFactory作为基类&#xff0c;定义了createPr…

设计模式之4 - 原型模式Proto Type

原型模式 核心价值在于复制&#xff08;clone接口&#xff09;&#xff0c;调用类通过调用原型的 clone接口&#xff0c;生成原型类的一个新的实例。 原型模式被用在对象的频繁拷贝上&#xff0c;其作用与拷贝构造函数类似&#xff08;Clone也用来屏蔽浅拷贝和深拷贝&#xff0…

Windows离线安装Python第三方库的方法

在window中&#xff0c;离线安装第三方模块&#xff0c; 1.下载第三方库的压缩文件&#xff0c;解压&#xff0c;将解压后的文件放到Python安装目录下的Lib\site_packages中 2. 将Python添加到环境变量里 3.进入到Python安装目录下Lib\site_packages中需要安装的第三方库文件目…

设计模式之5 - 单例模式Singleton

单例模式 是一种创建类型的模式&#xff0c;通过隐藏构造和析构函数&#xff0c;实现在内存中仅有一个对象 的目的。 Singleton的这种实现方式是非常有价值的&#xff0c;在多个模块或者多个线程中进行访问&#xff0c;可以不需要传递对象或者指针&#xff0c;通过引入头文件即…

mysql配置

SET sql_mode(SELECT REPLACE(sql_mode,ONLY_FULL_GROUP_BY,)); SET global sql_mode(SELECT REPLACE(sql_mode,ONLY_FULL_GROUP_BY,)); set global wait_timeout100; set global interactive_timeout300; set global max_connections600; 转载于:https://www.cnblogs.com/l…