C语言多线程基础-02-临界区,互斥量

一.多线程并发访问冲突

多线程并发访问冲突问题示例:

#include
#include
#include

//定义宏threadnumber用来设置创建线程的个数
#define threadnumber 50
//定义一个全局变量
int number;
DWORD WINAPI criticalFun(void *p);

//主方法
void main() {
    //创建有threadnumber(50)个元素的HANDLE数组
    HANDLE handle[threadnumber];
    //使用循环创建threadnumber(50)个线程,家用一次最多只能创建64个线程,即threadnumber的值不能大于64
    for (int i = 0; i < threadnumber; i++)
    {
        //创建一个线程返回一个句柄
        handle[i] = CreateThread(NULL, 0, criticalFun, NULL, 0, NULL);
    }
    //等待所有线程执行完毕
    WaitForMultipleObjects(threadnumber,handle,TRUE,INFINITE);
    printf("\nnumber = %d\n",number);
    system("pause");
}

DWORD WINAPI criticalFun(void *p) {
    int i = 1;
    while (i<1000)
    {
        //number递增
        number++;
        i++;
    }
    return 0;
}

上述程序开启了50个线程,每个线程都调用了number全局变量,并对number进行了递增操作,50个线程分别增加1000次则结果理论上应该为50000
程序执行结果:
第一次:
C语言多线程基础-02-临界区,互斥量_第1张图片

第二次:
C语言多线程基础-02-临界区,互斥量_第2张图片

发现两次执行结果不同,且都不是50000
这是因为发生了多线程并发访问冲突问题,即多个线程访问同一个全局变量时会使全局变量的值无法预测
解决多线程并发访问冲突的方法有很多,包括使用临界区,线程事件等方式

WaitForMultipleObjects方法和WaitForSingleObject方法:

概念及功能:

WaitForMultipleObjects方法和WaitForSingleObject方法是Windows API函数
WaitForMultipleObjects方法作用于多个线程,WaitForSingleObject作用于单个线程
两函数用来检测线程事件的信号状态(通过一个HANDLE类型的值),在某一线程中调用该函数时,线程暂时挂起,如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回,线程继续执行,如果超时时间已经到达dwMilliseconds毫秒,但线程所等待的对象还没有变成有信号状态,线程也继续执行
简单的说,就是设置条件,让使用该方法的线程停止,当条件满足或达到预定时间时,线程继续执行

定义及使用:

WaitForSingleObject方法的定义:

DWORD WINAPI WaitForSingleObject(
  _In_ HANDLE hHandle,
  _In_ DWORD  dwMilliseconds
);

WaitForMultipleObjects方法的参数

参数 含义
hHandle 目标线程的句柄
dwMilliseconds 最长等待时间,超过此限制后线程将不再等待,而是继续执行

WaitForMultipleObjects方法的定义:

DWORD WINAPI WaitForMultipleObjects(
  _In_       DWORD  nCount,
  _In_ const HANDLE *lpHandles,
  _In_       BOOL   bWaitAll,
  _In_       DWORD  dwMilliseconds
);

WaitForMultipleObjects方法的参数

参数 含义
nCount lpHandles中指向的数组中的对象句柄数,对象句柄的最大数目为 MAXIMUM_WAIT_OBJECTS(64),此参数不能为零
*lpHandles 一个句柄数组
bWaitAll 是否等待所有线程
dwMilliseconds 最长等待时间,超过此限制后线程将不再等待,而是继续执行

二.临界区

2.1 临界区的概念

临界区指的是一个访问共用资源的程序片段无法同时被多个线程访问的特性。
临界资源是一次仅允许一个进程使用的共享资源。每次只准许一个进程进入临界区,进入后不允许其他进程进入。不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它进行访问。

2.2 临界区涉及到的方法

4个方法的参数均为一个CRITICAL_SECTION类型的临界区变量
临界区的初始化和销毁:

方法 功能
InitializeCriticalSection 初始化一个临界区
DeleteCriticalSection 销毁一个临界区

进入和离开临界区,用于定义临界区的范围:

方法 功能
EnterCriticalSection 设置临界区的起始位置
LeaveCriticalSection 设置临界区的结束位置

2.3 使用临界区解决多线程冲突问题

#include
#include
#include
#include

int num = 0;

#define threadnumber 10
//声明CRITICAL_SECTION 临界区变量
CRITICAL_SECTION critical;

DWORD WINAPI criticalFun(void *p);

int main() {
    //初始化临界区
    InitializeCriticalSection(&critical);
    HANDLE handle[threadnumber];
    for (int i = 0; i < threadnumber; i++)
    {
        handle[i] = CreateThread(NULL, 0, criticalFun, NULL, 0, NULL);
    }
    WaitForMultipleObjects(threadnumber, handle, TRUE, INFINITE);
    //销毁临界区
    DeleteCriticalSection(&critical);
    printf("\nnumber = %d\n", num);
    system("pause");
}

DWORD WINAPI criticalFun(void *p) {
    int i = 1;
    //进入临界区
    EnterCriticalSection(&critical);
    while (i<1000) {
        num++;
        i++;
    }
    //离开临界区
    LeaveCriticalSection(&critical);
    return 0;
}

三.互斥量

3.1 互斥量的概念

互斥量是一个HANDLE 类型的值,它的使用方法和临界区类似,需要创建和删除。
互斥量就是多线程中锁的概念,对于一段定义了互斥量的程序,当某个线程的到这个互斥量时此线程才有执行权,当执行完毕后及时将互斥量释放,下一个线程获得互斥量并继续执行

3.2 互斥量涉及到的方法

方法 功能
CreateMutex() 创建一个互斥量
CloseHandle() 销毁一个互斥量
WaitForSingleObject 设置程序权限,只有获取了互斥量的线程才有执行权
ReleaseMutex 释放互斥量

3.3 使用临界区解决多线程冲突问题

#include
#include
#include
#include

#define threadnumber 54

int num = 0;
//声明一个互斥量
HANDLE mutex;

DWORD WINAPI mutexFun(void *p);

int main() {
    //CreateMutex创建一个互斥量,该方法的3个参数的含义及设置值依次:安全性设为NULL代表不设置安全性,是否立即激活设为FALSE不立即使用,id设为NULL不设置
    mutex = CreateMutex(NULL,FALSE,NULL);
    HANDLE handle[threadnumber];
    for (int i = 0; i < threadnumber; i++)
    {
        handle[i] = CreateThread(NULL, 0, mutexFun, NULL, 0, NULL);
    }
    WaitForMultipleObjects(threadnumber, handle, TRUE, INFINITE);
    //销毁互斥量
    CloseHandle(mutex);
    printf("\nnumber = %d\n", num);
    system("pause");
}

DWORD WINAPI mutexFun(void *p) {
    int i = 0;
    //设置权限,只有拿到互斥量的线程才可以执行
    WaitForSingleObject(mutex,INFINITE);
    while (i<1000) {
        num++;
        i++;
    }
    //释放互斥量,其他线程获取后继续执行
    ReleaseMutex(mutex);
    return 0;
}

你可能感兴趣的:(C/C++多线程)