精华内容
下载资源
问答
  • 互斥体
    2020-09-22 15:15:14
    1. 定义和初始化互斥体

    struct mutex my_mutex; //定义名为my_mutex的互斥体

    mutex_init(&my_mutex); //初始化

    1. 获取互斥体

    void fastcall mutex_lock(struct mutex *lock);

    //与前者的区别在于,后者引起的睡眠可被信号打断。

    int fastcall mutex_lock_interruptible(struct mutex *lock);

    //尝试获取mutex,获取不到mutex 时不会引起进程睡眠。

    int fastcall mutex_trylock(struct mutex *lock);

    1. 释放互斥体

    void fastcall mutex_unlock(struct mutex *lock);

    ##典型用法(与信号量完全一样)##

    struct mutex my_mutex; //定义

    mutex_init(&my_mutex); //初始化

    mutex_lock(&my_mutex); //获取mutex

    … //临界资源

    mutex_unlock(&my_mutex); //释放mutex

    更多相关内容
  • 例程是hook了CreateMutexA函数,创建互斥体时会自动修改互斥体名称。@冬夜静思念。Tags:CreateMutexA。
  • 易语言API创建互斥体禁止程序重复运行源码,API创建互斥体禁止程序重复运行,禁止重复运行,WaitForSingleObject,CreateMutex,ReleaseMutex,CloseHandle
  • 枚举并关闭互斥体游戏软件多开
  • Hook函数CreateMutexA拦截互斥体创建
  • 资源介绍:。易语言利用互斥体禁止程序重复运行,防止软件多开源码。资源作者:。@阿珏。资源下载:。
  • 创建互斥体防多开源码
  • 例程是hook了CreateMutexA函数,创建互斥体时会自动修改互斥体名称。
  • 一、信号量(Semaphore) 信号量(Semaphore)是由内核对象维护的int变量,当信号量为0时,在信号量上等待的线程会堵塞,信号量大于0时,就解除堵塞。当在一个信号量上等待的线程解除堵塞时,内核自动会将信号量的...
  • @upring。
  • 互斥体

    千次阅读 2020-09-13 19:39:08
    文章目录一、互斥体二、互斥体与线程锁的区别三、防止多开 一、互斥体 CreateMutex() //创建令牌 WaitForSingleObject() //获取令牌 ReleaseMutex() //释放令牌 #include "stdafx.h" #include "windows.h" int ...

    一、互斥体

    在这里插入图片描述
    在这里插入图片描述
    CreateMutex() //创建令牌

    1、安全描述符
    2、是否有信号(有信号为FALSE,没信号为TRUE)
    3、名称名(随便取)
    

    WaitForSingleObject() //获取令牌

    1、句柄
    2、等待时间
    

    ReleaseMutex() //释放令牌

    1、句柄
    

    演示:

    #include "stdafx.h"
    #include "windows.h"
    int main(int argc, char* argv[])
    {
        //创建令牌
        HANDLE g_mutex = CreateMutex(NULL,FALSE,"HEHE");
        //获取令牌
        WaitForSingleObject(g_mutex,INFINITE);
        for(int i=0;i<10;i++)
        {
            Sleep(1000);
            printf("这是A进程。。。。%d\n",i);
        }
        //释放令牌
        ReleaseMutex(g_mutex);
        return 0;
    }
    

    二、互斥体与线程锁的区别

    1、线程锁只能用于单个进程间的线程控制

    2、互斥体可以设定等待超时,但线程锁不能

    3、线程意外终结时,Mutex可以避免无限等待

    4、Mutex效率没有线程锁高

    三、防止多开

    1、创建令牌时,会有两种情况,成功会返回句柄,失败会返NULL
    2、成功又会有两种情况,
    成功1:返回句柄
    成功2:返回句柄和一个错误,ERROR_ALREADY_EXISTS
    3、防止多开,判断创建成功是否会有错误信息

    #include "stdafx.h"
    #include "windows.h"
    int main(int argc, char* argv[])
    {
    
    
        //创建令牌
        HANDLE g_mutex = CreateMutex(NULL,FALSE,"防止多开");
    	
    	DWORD DwRet = GetLastError();
    	if(g_mutex)
    	{
    		if(DwRet == ERROR_ALREADY_EXISTS)
    		{
    			CloseHandle(g_mutex);
    			return 0;
    		}		
    	}else
    	{
    		printf("创建失败");
    		CloseHandle(g_mutex);
    		return 0;
    	}
    	
        while(1)
        {
            Sleep(1000);
            printf("这是B进程。。。。\n");
        }   
        return 0;
    }
    
    
    
    展开全文
  • 易语言API创建互斥体禁止程序重复运行
  • 互斥体防多开源码.rar

    2020-08-06 05:27:45
    互斥体防多开源码.rar
  • 易语言利用互斥体禁止程序重复运行,防止软件多开源码。
  • 本文章是关于信号量、互斥体和自旋锁的区别。
  • 易语言-易语言互斥体防多开源代码
  • 例程_互斥体操作

    2013-04-10 00:49:46
    例程_互斥体操作 互斥体多开 互斥体句柄 互斥体查找 易语言源码
  • 首先我先介绍了下啥叫LSP注入,这种注入是凡是联网的程序都会被注入DLL,你同时开多个网络游戏,这几个 网络游戏都会被注入DLL,从而修改掉游戏实现游戏多开。
  • 我们可以把互斥体看作是内核中的令牌。 如图所示: 原理:当X线程访问临界资源时会先判断能否访问如果可以访问(得到令牌)那么就可以使用临界资源,这时Y线程判断是否可以访问,肯定是不行的那么Y线程就等待X线...

    1.内核级临界资源怎么办?

    之前我们的临界资源是一个全局变量,它是在同一个进程中由两个或者多个线程共享的,可是如果我们把临界资源放到内核中想要实现跨进程的共享怎么办呢?我们之前实现临界区的时候哪个进程拿到令牌哪个进程就使用临界资源。那么是不是在内核中也类似令牌的东西?

    2.互斥体的使用

    我们可以把互斥体看作是内核中的令牌。

    如图所示:

    原理:当X线程访问临界资源时会先判断能否访问如果可以访问(得到令牌)那么就可以使用临界资源,这时Y线程判断是否可以访问,肯定是不行的那么Y线程就等待X线程归还令牌。

    原理其实与之前的差不多,只不过把令牌放到了内核层中,因为我们知道内核层是共用的,这样才能实现不同进程访问临界资源。

    创建互斥体:CreateMutexA():

    HANDLE CreateMutexA(
      [in, optional] LPSECURITY_ATTRIBUTES lpMutexAttributes,//安全描述符
      [in]           BOOL                  bInitialOwner,//创建互斥体时有没有信号FALSE有TRUE没有
      [in, optional] LPCSTR                lpName//内核互斥体的名字(随便起)
    );

    ok下面我们使用互斥体创建临界区

    示例代码:

    #include"stdafx.h"
    #include<stdio.h>
    int main(int argc, char* argv[])
    {
        //创建互斥体
        HANDLE hMutex = CreateMutexA(NULL, FALSE, "wzp");
        //获取令牌
        WaitForSingleObject(hMutex, INFINITE);
        for (int i = 0; i < 10; i++)
        {
            Sleep(1000);
            printf("B进程的Y线程:%d \n", i);
    ​
        }
        //释放令牌
        ReleaseMutex(hMutex);
        return 0;
    }

    我们把这个代码放到两个不同的进程中看看效果

    输出:我们们先运行exam。在运行Project2

    如上图当exam运行时Project2是等待状态的

     

    exam运行完Project2开始运行

    下面我们再来介绍一下WaitForSingleObject()怎么是获取令牌(因为我想了很久估计是太笨了所以记录下)

    我们在线程控制中了解到这个函数,那时我们用它等待线程的结束。
    这里怎么实现获得令牌的呢?
    我查了下官方文档发现:WaitForSingleObject函数检查指定对象的当前状态。
    如果对象的状态为非信号,则调用线程将进入等待状态,直到向对象发出信号或
    超时间隔过去。上面这句意思就是看看我们给它传进去的对象有没有信号有的话
    就跳过没的话就等待。我们之前等待线程就是线程在运行中没信号等运行完有信
    号了就跳过。这里我们创建互斥体中的第二个参数bInitialOwner填为FALSE意
    思就是一开始就有信号。当我们先运行exam时它拿到令牌又因为互斥体是在内核
    对象中的是唯一的,那么Project2就拿不到令牌也就是找不到信号。等exam结束
    释放令牌又有了信号所以Project2拿到令牌执行。

    简单来说就是用WaitForSingleObject()申请对互斥体的所有权用ReleaseMutex()释放对互斥体的所有权

    下面再来学习一下CreateMutexA()的第二个参数 bInitialOwner我们知道这个参数填FALSE是有信号填TRUE是没信号。我们来试试填TRUE

    exam中代码如下:

    #include"stdafx.h"
    #include<stdio.h>
    int main(int argc, char* argv[])
    {
        HANDLE hMutex = CreateMutexA(NULL, TRUE, "wzp");//这里改成TRUE
        getchar();
        return 0;
    }

    Project2中代码如下:

    #include"stdafx.h"
    #include<stdio.h>
    int main(int argc, char* argv[])
    {
        //创建互斥体
        HANDLE hMutex = CreateMutexA(NULL, FALSE, "wzp");
        //获取令牌
        WaitForSingleObject(hMutex, INFINITE);
        for (int i = 0; i < 10; i++)
        {
            Sleep(1000);
            printf("A进程的X线程:%d \n", i);
    ​
        }
        //释放令牌
        ReleaseMutex(hMutex);
        return 0;
    }

    我们先执行exam注意一定要先执行exam因为我们要首先创建没信号的互斥体如果先执行Project2肯定是能运行的。

    输出:

    如图两个都没反应。因为我们创建了没信号的互斥体那么WaitForSingleObject()就一直等待了,如果我们把TRUE改为FALSE肯定是能运行的,因为互斥体能传过去。

    下面我们在试试这样:

     

    #include"stdafx.h"
    #include<stdio.h>
    int main(int argc, char* argv[])
    {
        //创建互斥体
        HANDLE hMutex = CreateMutexA(NULL, TRUE, "wzp");//填的TREU
        //获取令牌
        WaitForSingleObject(hMutex, INFINITE);
        for (int i = 0; i < 10; i++)
        {
            Sleep(1000);
            printf("A进程的X线程:%d \n", i);
    ​
        }
        //释放令牌
        ReleaseMutex(hMutex);
        return 0;
    }

    我们直接运行这个程序

    发现也可以运行,这时为什么?

    原来填TRUE的时候不仅代表着没信号也意味着这个互斥体是属于当前进程的。那么我们想获取令牌就有两种情况

    获取令牌:
        <1>有信号
        <2>线程拥有者

    3.互斥体和线程锁的区别:

    <1>线程锁只能用于单个进程间的线程控制//不解释了就是我们在临界区所学的

    <2>互斥体可以设定等待超时,但线程锁不能:我们知道WaitForSingleObject()中第二个参数可以设定等待时间如果超过这个时间还在等待的话也会返回

    <3>线程意外终结时,Mutex可以避免无线等待:就假如一个线程获取了令牌在执行是因为某种原因终止了(还没有来得及释放令牌)那么互斥体还是会释放令牌。我们举个例子:

    把下面这个放到Project2中

    #include"stdafx.h"
    #include<stdio.h>
    int main(int argc, char* argv[])
    {
        //创建互斥体
        HANDLE hMutex = CreateMutexA(NULL, FALSE, "wzp");
        //获取令牌
        WaitForSingleObject(hMutex, INFINITE);
        for (int i = 0; i < 10; i++)
        {
            Sleep(1000);
            if (i == 8)//我让i=8时进程终止
            {
                ExitProcess(0);
            }
            printf("A进程的X线程:%d \n", i);
        }
        //释放令牌
        ReleaseMutex(hMutex);
        return 0;
    }

    下面的放到exam中

    #include"stdafx.h"
    #include<stdio.h>
    int main(int argc, char* argv[])
    {
        //创建互斥体
        HANDLE hMutex = CreateMutexA(NULL, FALSE, "wzp");
        //获取令牌
        WaitForSingleObject(hMutex, INFINITE);
        for (int i = 0; i < 10; i++)
        {
            Sleep(1000);
            printf("B进程的Y线程:%d \n", i);
    ​
        }
        //释放令牌
        ReleaseMutex(hMutex);
        getchar();
        return 0;
    }

    我们先执行Project2再执行exam

    输出:

    如图即使意外终止了依然能执行线程B。

    如果是线程锁的话那么就一直等待了。

    <4>Mutex的效率没有线程锁高

    4.防止多开

    在介绍之前,我学习了一下为什么两个程序中都创建了一样名字的互斥体。看了下官方文档

    如果函数成功,则返回值是新创建的互斥对象的句柄。
    如果函数失败,则返回值为NULL。要获取扩展的错误信息,请致电GetLastError。
    如果互斥体是命名互斥体,并且该对象在此函数调用之前已存在,则返回值是现有对象的句柄,GetLastError函数返回ERROR_ALREADY_EXISTS。

    原来当我们第一次创建肯定成功了返回一个句柄,如果我们再次创建一定是失败的,不过如果是已经命名的话也会返回一个句柄(就是我创建的”wzp“)并且GetLastError函数返回一个ERROR_ALREADY_EXISTS

    下面我们介绍什么是防止多开。举个例子你如果玩游戏的话有些作弊的是不是喜欢把一个游戏开好多窗口刷刷首抽什么的。那么游戏制作者肯定不喜欢你这样做(你这样搞我很难赚钱哎)于是就有防止一个游戏多开的手段。手段有很多互斥体是其中一中。我们就可以利用上面介绍的GetLastError函数返回一个ERROR_ALREADY_EXISTS来防止多开。

    示例代码:

    #include"stdafx.h"
    #include<stdio.h>
    int main(int argc, char* argv[])
    {
        
        while (1)
        {
            Sleep(1000);
            printf("程序执行中\n");
        }
        return 0;
    }

    我们先像上图这样在两个进程中执行

    输出

     

    可以看到是完全没问题的,在来试试下面的

    #include"stdafx.h"
    #include<stdio.h>
    int main(int argc, char* argv[])
    {
        HANDLE hMutex = CreateMutexA(NULL, FALSE, "防止多开");
        DWORD dwRet = GetLastError();
        if (hMutex)
        {
            if (ERROR_ALREADY_EXISTS == dwRet)
            {
                CloseHandle(hMutex);
                return 0;
            }
        }
        else
        {
            printf("创建失败,程序退出!\n");
            return 0;
        }
        while (1)
        {
            Sleep(1000);
            printf("程序执行中\n");
        }
        return 0;
    }

    当我们第一次创建时,hMutex是句柄值GetLastError是0这样肯定能成功运行。但是当我们重复创建时候虽然hMutex还是句柄值不过GetLastError=ERROR_ALREADY_EXISTS 这时程序就会退出。

    输出:

    可以看到只有先打开的程序能运行,在多开一个直接退出了。这样就可以防止多开

    展开全文
  • 互斥体globalmem的实现

    2022-09-06 21:14:26
    互斥体globalmem的实现,上个mutex而已,功能有read,write,ioctl,open,llseek这几个,其他的没写完,自己看着加吧,没钱直接去我gitee找源码。
  • killmutex删除互斥体

    2016-07-23 17:48:43
    遍历进程干掉互斥体
  • MPI的互斥体仿真 用法 该互斥锁旨在锁定特定的远程内存(RM),例如等级0。这意味着充当互斥锁的布尔值位于特定的等级中。但是,当获取关键部分时,任何其他通信都可以是过程安全的。 然后,确保创建将与相应等级...
  • 条件变量 条件变量为什么要与互斥体结合使用 我们假设条件变量不与互斥体结合,看看效果: 伪代码: // m是互斥体,cv是条件变量 pthread_mutex_lock(&m); while (condition_is_false) { pthread_mutex_unlock(&m);...

    1. Linux互斥体

    Linux互斥体与Windows的临界区对象用法很相似,一般也是通过限制多个线程同时执行某段代码来保护资源的。使用数据结构pthread_mutex_t表示一个互斥体对象(pthread.h中)。

    初始化方式

    1. 使用 PTHREAD_MUTEX_INITIALIZER直接给互斥体变量赋值,示例
    #include <pthread.h>
    pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
    
    1. 若互斥体是动态分配的或者需要设置相关属性,需要使用pthread_mutex_init函数初始化:
    int pthread_mutex_init(pthread_mutex_t* restrict mutex,
    					const pthread_mutexattr_t* restrict attr);
    

    参数mutex是需要初始化的mutex对象的指针
    attr是需要设置的互斥体属性,通常设置为NULL。
    如果执行成功,返回0;失败返回1个错误吗。

    使用示例:

    #include <pthread.h>
    
    pthread_mutex_t mtx;
    pthread_mutex_init(&mtx, NULL);
    

    销毁方式

    当不再需要互斥体对象时,使用pthread_mutex_destroy函数销毁它:

    int pthread_mutex_destroy(pthread_mutex_t* mutex);
    

    执行成功返回0,失败返回错误码。

    注意点:

    1. 无需销毁使用PTHREAD_MUTEX_INITIALIZER初始化的互斥体。
    2. 不要销毁一个已经加锁或正在被条件变量使用的互斥体对象,当互斥体处于已加锁状态或正在和条件变量配合使用时,调用销毁函数会返回 EBUSY错误。

    加解锁操作

    一般使用三个函数:

    int pthread_mutex_lock(pthread_mutex_t* mutex);
    int pthread_mutex_trylock(pthread_mutex_t* mutex);
    int pthread_mutex_unlock(pthread_mutex_t* mutex);
    

    他们都是执行成功返回0,失败则返回错误码。具体错误码随着互斥体对象属性类型的不同而不同。

    在设置互斥体对象属性时需要创建一个pthread_mutexattr_t类型的对象:

    int pthread_mutexattr_init(pthread_mutexattr_t* attr);
    int pthread_mutexattr_destroy(pthread_mutexattr_t* attr);
    

    使用pthread_mutexattr_settype/gettype设置或获取想要的属性类型。

    int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type);
    int pthread_mutexattr_gettype(const pthread_mutexattr* restrict attr, 
    						int* restrict type); 
    

    设置属性

    属性类型一般有以下这些值。

    1. PTHREAD_MUTEX_NORMAL(普通锁)

    一个线程如果对一个已经加锁的普通锁再次加锁,那么程序会阻塞在第二次调用pthread_mutex_lock代码处。测试:

    #include <pthread.h>
    #include <stdio.h>
    #include <errno.h>
    #include <unistd.h>
    
    int main(void)
    {
        pthread_mutex_t mymutex;
        pthread_mutexattr_t mutex_attr;
        pthread_mutexattr_init(&mutex_attr);
        pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_NORMAL);
        pthread_mutex_init(&mymutex, &mutex_attr);
    
        int ret = pthread_mutex_lock(&mymutex);
        printf("ret = %d\n", ret);
    
        // 再次加锁
        ret = pthread_mutex_lock(&mymutex);
        printf("ret = %d\n", ret);
    
        pthread_mutex_destroy(&mymutex);
        pthread_mutexattr_destroy(&mutex_attr);
    
        return 0;
    }
    

    运行结果只打印了一行,原因是阻塞在第二个lock处。

    这种情况下,pthread_mutex_trylock()如果拿不到锁,则也不会阻塞,而是会立即返回EBUSY错误码。

    在这里插入图片描述

    在这里插入图片描述

    1. PTHRAED_MUTEX_ERRORCHECK(检错锁)

    如果是这种属性的锁,对于已经加锁的互斥体对象再次加锁,则pthread_mutex_lock会返回EDEADLK。

    示例:

    #include <pthread.h>
    #include <stdio.h>
    #include <errno.h>
    #include <unistd.h>
    
    int main(void)
    {
        pthread_mutex_t mymutex;
        pthread_mutexattr_t mutex_attr;
        pthread_mutexattr_init(&mutex_attr);
        pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK);
        pthread_mutex_init(&mymutex, &mutex_attr);
    
        int ret = pthread_mutex_lock(&mymutex);
        printf("ret = %d\n", ret);
    
        // 再次加锁
        ret = pthread_mutex_lock(&mymutex);
        printf("ret = %d\n", ret);
        if (ret == EDEADLK)
        {
            printf("EDEADLK\n");
        }
    
        pthread_mutex_destroy(&mymutex);
        pthread_mutexattr_destroy(&mutex_attr);
    
        return 0;
    }
    

    在这里插入图片描述

    如果是其他线程对这个互斥体再次调用lock,则会阻塞在该函数的调用处。

    1. PTHREAD_MUTEX_RECURSIVE(可重入锁)

    该属性允许同一个线程对其持有的互斥体重复加锁,每成功调用lock一次,该互斥体对象的锁引用技术就会增加1,相反,unlock一次,引用技术减1。引用计数为0时,允许其他线程获取该锁,否则会阻塞在lock调用处。

    2.Linux信号量

    信号量代表一定的资源数量,可以根据当前资源的数量按需唤醒指定数量的资源消费者线程,资源消费者线程一旦获取信号量,就会让资源减少指定的数量,如果减少为0,则消费者线程将全部处于挂起状态;当有新的资源到来时,消费者线程将继续被唤醒。
    另外,信号量有“资源有多份,可以同时被多个线程访问”的意思。

    常用API函数:

    #include <semaphore.h>
    
    int sem_init(sem_t* sem, int pshared, unsigned int value);
    int sem_destroy(sem_t* sem);
    int sem_post(sem_t* sem);
    int sem_wait(sem_t* sem);
    int sem_trywait(sem_t* sem);
    int sem_timedwait(sem_t* sem, const struct timespec* abs_timeout);
    

    sem_int()第一个参数是信号量对象地址,第二个参数表示该信号量是否可以被共享,取值为0表示只能在同一个进程的多个线程共享,非0可以在多进程间共享,第三个参数表示初始状态资源数量。 函数调用成功返回0,失败返回-1。

    sem_destroy()销毁信号量;
    sem_post()用于将信号量的资源计数递增1,并解锁该信号量对象,这样因使用sem_wait()被阻塞的其他线程会被唤醒。

    如果当前资源计数为0,sem_wait()会阻塞调用线程,直到大于0时被唤醒,唤醒后资源计数-1后返回;

    sem_trywait()是非阻塞版本,如果资源计数为0,则立即返回,不阻塞调用线程,返回值是-1,错误码errno被设置成EAGAIN;

    sem_timedwait()带有等待时间的版本,第二个参数是个结构体:

    struct timespec
    {
    	time_t tv_sec;	// 秒
    	long tv_nsec;	// 纳秒 范围[0~999999999]
    };
    

    信号量实现生产者消费者

    #include <pthread.h>
    #include <errno.h>
    #include <unistd.h>
    #include <semaphore.h>
    #include <list>
    #include <iostream>
    
    class Task
    {
    public:
        Task(int taskID)
        {
            this->taskID = taskID;
        }
    
        void doTask()
        {
            std::cout << "handle a task, taskID: " << taskID << ", threadID: " << pthread_self() << std::endl;
        }
    
    private:
        int taskID;
    };
    
    pthread_mutex_t     mymutex;
    std::list<Task*>    tasks;
    sem_t               mysemaphore;
    
    void* consumer_thread(void* param)
    {
        Task* pTask = NULL;
        while (true)
        {
            if (sem_wait(&mysemaphore) != 0)
            {
                continue;
            }
            if (tasks.empty())
            {
                continue;
            }
    
            pthread_mutex_lock(&mymutex);
            pTask = tasks.front();
            tasks.pop_front();
            pthread_mutex_unlock(&mymutex);
    
            pTask->doTask();
            delete pTask;
        }
    
        return NULL;
    }
    
    void* producer_thread(void* param)
    {
        int taskID = 0;
        Task* pTask = NULL;
    
        while (true)
        {
            pTask = new Task(taskID);
    
            pthread_mutex_lock(&mymutex);
            tasks.push_back(pTask);
            std::cout << "produce a task, taskID: " << taskID << ", threadID: " << pthread_self() << std::endl;
    
            pthread_mutex_unlock(&mymutex);
    
            // 释放信号量,通知消费者线程
            sem_post(&mysemaphore);
    
            taskID++;
    
            sleep(1);
        }
    
        return NULL;
    }
    
    int main(void)
    {
        pthread_mutex_init(&mymutex, NULL);
        // 初始化信号量资源计数为0
        sem_init(&mysemaphore, 0, 0);
    
        // 创建5个消费者线程
        pthread_t consumerThreadID[5];
        for (int i = 0; i < 5; ++i)
        {
            pthread_create(&consumerThreadID[i], NULL, consumer_thread, NULL);
        }
    
        // 创建一个生产者线程
        pthread_t producerThreadID;
        pthread_create(&producerThreadID, NULL, producer_thread, NULL);
    
        pthread_join(producerThreadID, NULL);
    
        for (int i = 0; i < 5; ++i)
        {
            pthread_join(consumerThreadID[i], NULL);
        }
    
        sem_destroy(&mysemaphore);
        pthread_mutex_destroy(&mymutex);
    
        return 0;
    }
    

    输出结果:

    在这里插入图片描述

    3.条件变量

    条件变量为什么要与互斥体结合使用

    我们假设条件变量不与互斥体结合,看看效果:

    伪代码:

    // m是互斥体,cv是条件变量
    pthread_mutex_lock(&m);
    while (condition_is_false)
    {
    	pthread_mutex_unlock(&m);
    	// 解锁之后,等待之前,可能条件已经满足,信号已经发出,但该信号可能被错过
    	cond_wait(&cv);
    	pthread_mutex_lock(&m);
    }
    

    假设线程A在执行完第五行代码后CPU时间片被剥夺,此时另一个线程B获得互斥体m,,然后发送条件信号,等线程A重新获得时间片后,由于该信号已经被错过,可能会导致线程A在第七行无限阻塞下去。

    造成这一现象的根源是释放互斥体对象与条件变量等待唤醒不是原子操作。

    条件变量的使用

    初始化和销毁:

    int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr);
    int pthread_cond_destroy(pthread_cond_t* cond);
    

    也可以这样初始化:

    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    

    等待条件变量被唤醒:

    int pthread_cond_wait(pthread_cond_t* restrict cond, pthread_mutex_t* restrict mutex);
    int pthread_cond_timedwait(pthread_cond_t* restrict cond, pthread_mutex_t*
    						restrict mutex, const struct timespec* restrict abstime);
    

    如果条件变量等待的条件没有被满足,那么调用pthread_cond_wait()的线程会一直等待下去。
    timedwait()是非阻塞版本。

    因调用pthread_cond_wait()而等待的线程可以被以下API函数唤醒:

    int pthread_cond_signal(pthread_cond_t* cond);
    int pthread_cond_broadcast(pthread_cond_t* cond);
    

    signal表示一次唤醒一个线程,而且是随机的;
    broadcast唤醒所有调用pthread_cond_wait()等待的线程,相当于广播。。。

    条件变量实现生产者消费者

    将刚才信号量的代码稍微改一改~

    #include <pthread.h>
    #include <errno.h>
    #include <unistd.h>
    #include <semaphore.h>
    #include <list>
    #include <iostream>
    
    class Task
    {
    public:
        Task(int taskID)
        {
            this->taskID = taskID;
        }
    
        void doTask()
        {
            std::cout << "handle a task, taskID: " << taskID << ", threadID: " << pthread_self() << std::endl;
        }
    
    private:
        int taskID;
    };
    
    pthread_mutex_t     mymutex;
    std::list<Task*>    tasks;
    pthread_cond_t      mycv;
    //sem_t               mysemaphore;
    
    void* consumer_thread(void* param)
    {
        Task* pTask = NULL;
        while (true)
        {
            pthread_mutex_lock(&mymutex);
            while (tasks.empty())
            {
                // 如果获得了互斥锁,但是条件不合适,则pthread_cond_wait会释放锁,不往下执行
                // 发生变化后,如果条件合适,则pthread_cond_wait直接获得锁
                pthread_cond_wait(&mycv, &mymutex);
            }
    
            pTask = tasks.front();
            tasks.pop_front();
    
            pthread_mutex_unlock(&mymutex);
    
            if (pTask == NULL)
                continue;
    
            pTask->doTask();
            delete pTask;
            pTask = NULL;
        }
    
        return NULL;
    }
    
    void* producer_thread(void* param)
    {
        int taskID = 0;
        Task* pTask = NULL;
    
        while (true)
        {
            pTask = new Task(taskID);
    
            pthread_mutex_lock(&mymutex);
            tasks.push_back(pTask);
            std::cout << "produce a task, taskID: " << taskID << ", threadID: " << pthread_self() << std::endl;
    
            pthread_mutex_unlock(&mymutex);
    
            // 释放信号量,通知消费者线程
            //sem_post(&mysemaphore);
            pthread_cond_signal(&mycv);
    
            taskID++;
    
            sleep(1);
        }
    
        return NULL;
    }
    
    int main(void)
    {
        pthread_mutex_init(&mymutex, NULL);
        pthread_cond_init(&mycv, NULL);
    
        // 创建5个消费者线程
        pthread_t consumerThreadID[5];
        for (int i = 0; i < 5; ++i)
        {
            pthread_create(&consumerThreadID[i], NULL, consumer_thread, NULL);
        }
    
        // 创建一个生产者线程
        pthread_t producerThreadID;
        pthread_create(&producerThreadID, NULL, producer_thread, NULL);
    
        pthread_join(producerThreadID, NULL);
    
        for (int i = 0; i < 5; ++i)
        {
            pthread_join(consumerThreadID[i], NULL);
        }
    
        //sem_destroy(&mysemaphore);
        pthread_cond_destroy(&mycv);
        pthread_mutex_destroy(&mymutex);
    
        return 0;
    }
    

    在这里插入图片描述

    条件变量最关键的一个地方,就是需要弄清楚pthread_cond_wait()在条件满足于不满足时的两种行为:

    1. pthread_cond_wait()在阻塞时,会释放器绑定的互斥体并阻塞线程。因此在调用该函数前应该对互斥体有个加锁操作。
    2. 收到条件信号时,pthread_cond_wait()会返回并对其绑定的互斥体进行加锁,因此在其下面一定由个对互斥体解锁的操作。

    条件变量的虚假唤醒

    再将互斥体和条件变量配合使用的代码中,有个while()语句,条件变量醒来之后再次判断条件是否满足:

    while (tasks.empty())
    {
    	pthread_cond_wait(&mycv, &mymutex);
    }
    

    为什么不写成下面代码呢?

    if (tasks.empty()) {...}
    

    原因:
    因为某次操作系统唤醒pthread_cond_wait()时tasks.empty()可能仍为true,即操作系统可能在某些情况下唤醒条件变量,也就是说存在没有其他线程向条件变量发送信号,但等待此条件变量的线程有可能醒来的情形。我们将这种行为称为 虚假唤醒(spurious wakeup)。
    因此将条件放在while循环中意味着光唤醒条件变量不行,还必须满足条件,程序才能继续执行正常逻辑。

    为什么会存在虚假唤醒?
    一个原因是pthread_cond_wait()是futex系统调用,属于阻塞型系统调用,当系统调用被信号中断时,会返回-1,并把errno错误码置为EINTR。

    4. 读写锁

    读写锁的应用场景

    在实际应用中,对共享变量的访问大多有个特点:大多数情况下,线程只是读取共享变量的值,只有在极少数情况下才会真正修改共享变量的值。对于这种情况,读请求之间无须同步,他们之间的并发访问是安全的。然而写请求必须锁住读请求和其他写请求。

    读写锁的应用方法

    读写锁在Linux系统中使用pthread_rwlock_t类型表示,初始化和销毁API:

    #include <pthread.h>
    
    int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr* attr);
    
    int pthread_rwlock_destroy(pthread_rwlock_t* rwlock);
    

    rwlock参数是需要初始化和销毁的读写锁对象的地址,attr参数用于设置读写锁的属性,一般设置为NULL,表示使用默认属性。若函数调用成功返回0,失败返回非0值,通过errno错误码判断错误原因。

    如果不需要动态创建或者设置非默认属性的读写锁对象,可以使用:

    pthread_rwlock_t myrwlock = PTHREAD_RWLOCK_INITIALIZER;
    

    请求读锁的系统API:

    int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);
    int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock);
    int pthread_rwlock_timedrdlock(pthread_rwlock_t* rwlock, const struct timespec* abstime);
    

    请求写锁的系统API:

    int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
    int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock);
    int pthread_rwlock_timedwrlock(pthread_rwlock_t* rwlock, const struct timespec* abstime);
    

    读锁用于共享模式:如果当前读写锁已经被某线程以读模式占有,则其他线程调用【请求读锁】时会立刻获得读锁;如果当前读写锁已经被某线程以读模式占有,则其他线程调用pthread_rwlock_wrlock【请求写锁】时会陷入阻塞;

    写锁用于独占模式:如果当前读写锁被某线程以写模式占有,则无论是调用rdlock还是wrlock,都会陷入阻塞。即在写模式下不允许任何读锁请求通过,读锁请求和写锁请求都要陷入阻塞中,直到线程释放写锁。

    展开全文
  • /*释放信号量*/ 三、互斥体简介 在FreeRTOS和UCOS中也有互斥体,将信号量的值设置为1就可以使用信号量进行互斥访问了,虽然可以通过信号量实现互斥,但是Linux提供了一个比信号量更专业的机制来进行互斥,它就是互斥...
  • 互斥体操作

    2014-06-22 05:20:24
    揭秘互斥体多开,很多游戏用它限制多开,提供多开源码
  • 我们可以利用创建互斥体(Mutex)的方法,第一个被启动的进程发现当前没有创建过某个名字的互斥体(比如我们给互斥体起名叫MyTestMutex)之后,就会创建这个互斥体,等到这个程序再被启动的时候发现当前存在一个指定...
  • 互斥体无限多开

    2014-08-13 21:13:03
    编程实现互斥体无限多开,未用驱动。用易语言编写
  • 临界区和互斥体

    2020-02-26 18:13:10
    Win32中临界区和互斥体的产生是为了解决不同线程或进程访问全局变量或其公共资源产生错误的问题。 1. 临界区 临界区的产生是为了解决一个进程内多个线程访问全局变量的问题。由于同一个进程中不同线程拥有各自的堆栈...
  • 【2021.01.03】互斥体

    2021-01-03 23:42:27
    内核级临界资源怎么办? 如果这个临界资源是内核级临界资源,...互斥体的使用 CreateMutex 创建一个互斥体。 HANDLE CreateMutexA( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCSTR l

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 48,752
精华内容 19,500
关键字:

互斥体

友情链接: 23503548.rar