一、互斥锁

1、先熟悉熟悉API

1,创建互斥锁,并反正一个句柄

HANDLE CreateMutex(
LPSECURITY_ATTRIBUTESlpMutexAttributes, // 指向安全属性的指针
BOOLbInitialOwner, // 初始化互斥对象的所有者,一般设置为FALSE
LPCTSTRlpName // 互斥对象名
);

2,释放互斥对象的控制权

BOOL ReleaseMutex(
HANDLE hMutex //已创建Mutex的句柄
);

3,打开互斥锁,返回句柄

HANDLE OpenMutex(
DWORDdwDesiredAccess, // 互斥体的访问权限
BOOLbInheritHandle, //如希望子进程能够继承句柄,则为TRUE
LPCTSTRlpName // 互斥锁名字
);

4,等待目标返回

DWORD WaitForSingleObject(
HANDLE hHandle, //目标句柄
DWORD dwMilliseconds //等待时间 ,毫秒记,INFINITE为永久等待
);

    一个简单的互斥锁例子来实现多线程同步

    

#include<Windows.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
static int g_count = 100;

class Lock
{
public:
    Lock(char*szName)
    {
        hd = OpenMutex(MUTEX_ALL_ACCESS, NULL, (LPCWSTR)szName);
        WaitForSingleObject(hd, INFINITE);
        Sleep(1500);//测试用:等待1.5秒,正式类中不应有
    }

    ~Lock()
    {
        ReleaseMutex(hd);
    }
    static bool InitLock(char*szName)
    {
        if (!OpenMutex(MUTEX_ALL_ACCESS, NULL, (LPCWSTR)szName))
        {
            if (!CreateMutex(NULL, NULL, (LPCWSTR)szName))
                return false;
            return true;
        }
        return false;
    }
    HANDLE hd;
};


UINT address1(LPVOID lparam)
{
    while (1)
    {
        Lock lc("_temp_lock");
        cout << g_count-- << "---" << "address1" << endl;
    }
}

UINT address2(LPVOID lparam)
{
    while (1)
    {
        Lock lc("_temp_lock");
        cout << g_count-- << "---" << "address2" << endl;
    }
}
int main(int argc, char*argv[])
{
    if(!Lock::InitLock("_temp_lock"))//创建互斥锁失败
        return -1;
    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)address1, NULL, NULL, NULL);//开启线程1
    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)address2, NULL, NULL, NULL);//开启线程2


    system("pause");
    return 0;
}

原理浅析:Lock::InitLock()来创建互斥锁,在本demo中,同步是按照CreateThread的创建顺序。创建了一个Lock类,当生成对象的时候就等待,一旦当互斥锁没有控制权,等待即结束,便向下执行,对象析构的时候就释放控制权,以此类推循环。
ps:mutex作用范围在系统层,使用mutex效率并不是太高,使用临界区(作用范围在进程)效率比较高
二、信号量

1,先熟悉熟悉API

CreateSemaphore() 创建一个信号量  

OpenSemaphore() 打开一个信号量  

ReleaseSemaphore() 释放信号量 

WaitForSingleObject() 等待信号量  

2,举个简单的小例子

    

#include <stdio.h>  
#include <Windows.h>  


#define THREAD_NUM 20
#define SEM_NAME "THREAD_SEMAPHORE"

HANDLE g_hSem = NULL;
HANDLE g_hThread[THREAD_NUM];


void WINAPI ThreadFun(void* param)
{
    printf("进入线程: %u,并等待\n", GetCurrentThreadId());
    WaitForSingleObject(g_hSem, INFINITE);

    printf("线程: %u 获得信号量\n", GetCurrentThreadId());

    long dwSem = 0;
    if (!ReleaseSemaphore(g_hSem, 1, &dwSem))
        return;

    printf("目前资源数:%u\n", dwSem);
}

int  main(int argc, char*argv[])
{
    g_hSem = CreateSemaphoreA(NULL, 0, 5, SEM_NAME);
    g_hSem = OpenSemaphoreA(SEMAPHORE_MODIFY_STATE, FALSE, SEM_NAME);

    for (int i = 0; i < THREAD_NUM; ++i)
    {
        DWORD dwThreadID = 0;
        g_hThread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFun, NULL, 0, &dwThreadID);
    }

    WaitForMultipleObjects(THREAD_NUM, g_hThread, TRUE, INFINITE);

    printf("全部执行完了\n");
    return 0;
}

ps:信号量可以说在平常的使用中用的比较少,一般的用途在控制并发线程数量,抢占资源问题。
三、事件

1,先熟悉熟悉API

HANDLE CreateEvent( //创建事件
LPSECURITY_ATTRIBUTES lpEventAttributes,// 安全属性
BOOL bManualReset,// 复位方式
BOOL bInitialState,// 初始状态
LPCTSTR lpName // 对象名称
);

HANDLE OpenEvent( //打开事件
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);

BOOL ResetEvent(//事件对象设置为无信号状态。
HANDLE hEvent
);

BOOL SetEvent(HANDLE hEvent);//事件对象设置为有信号状态。 

BOOL PulseEvent(HANDLE hEvent)//事件对象设置为有信号状态,脉冲一个事件

WaitForSingleObject() 等待信号量  

2,举个简单的小例子

    

#include <stdio.h>
#include <Windows.h>
HANDLE g_hEvent;

UINT address1(LPVOID lparam)
{
    printf("进入线程了,等待5秒\n");
    WaitForSingleObject(g_hEvent, INFINITE);
    printf("等待结束了,向下执行了!\n");
    return 0;
}

UINT address2(LPVOID lparam)
{
    Sleep(5000);
    SetEvent(g_hEvent);
    return 0;
}

int main(int argc, char*argv[])
{
    g_hEvent=CreateEvent(NULL, true, FALSE, NULL);
    ResetEvent(g_hEvent);

    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)address1, NULL, NULL, NULL);//开启线程1
    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)address2, NULL, NULL, NULL);//开启线程2

    system("pause");
    return 0;
}

ps:本小例模拟了的两个线程间的同步,address2来控制address1的执行,事件在多线程程序中的应用比较多,比较重要,定要熟练掌握。
四、临界区

1,先熟悉熟悉API

void InitializeCriticalSection(//初始化临界区
 LPCRITICAL_SECTION lpCriticalSection); 

void WINAPI DeleteCriticalSection( //删除临界区  
_Inout_ LPCRITICAL_SECTION lpCriticalSection);

VOID WINAPI EnterCriticalSection( //进入临界区  
    __inout LPCRITICAL_SECTION lpCriticalSection
); 

VOID WINAPI LeaveCriticalSection(//离开临界区
    _Inout_ LPCRITICAL_SECTION lpCriticalSection
    );

2,举个简单的小例子

#include <stdio.h>
#include <Windows.h>

CRITICAL_SECTION g_cs;
int g_nIndex = 20;

UINT address1(LPVOID lparam)
{
    while(true)
    {
        if (10 == g_nIndex)
            return 1;
        EnterCriticalSection(&g_cs);
        printf("address111线程,index=%d\n",g_nIndex);
        g_nIndex--;
        LeaveCriticalSection(&g_cs);
    }
    return 0;

}

UINT address2(LPVOID lparam)
{
    while (true)
    {
        if (0 == g_nIndex)
            return 1;
        EnterCriticalSection(&g_cs);
        printf("address222线程,index=%d\n", g_nIndex);

        --g_nIndex;
        LeaveCriticalSection(&g_cs);
    }
    return 0;
}
int main(int argc, char*argv[])
{
    InitializeCriticalSection(&g_cs);

    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)address1, NULL, NULL, NULL);//开启线程1
    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)address2, NULL, NULL, NULL);//开启线程2

    system("pause");
    DeleteCriticalSection(&g_cs);
    return 0;
}

 

ps:小例中,address1被临界区一直占用到到address1退出,address2才能进入临界区,进行直到退出,与mutex交替执行不同,临界区比较简单且最易用。
这里写图片描述
这里写图片描述

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐