线程互斥
前言:线程互斥的学习笔记,今天重新看了下这篇笔记发现很多需要缺漏的,今天重新写一篇
线程安全问题:当多个线程对同一个全局变量(临界资源)进行操作,并且同时进行读写的操作的时候所引发的数据不统一问题
需要提前知道的几个学术上的概念
临界资源:临界资源是一次仅允许一个进程使用的资源称为临界资源
临界区:访问临界资源的代码区称为临界区
存在线程安全问题的代码
#include<windows.h>
#include<stdio.h>
int Tickets = 10;
DWORD WINAPI MyThreadFun_1(LPVOID pParameter) {
while (Tickets > 0){
printf("当前票还剩余%d张 Thread-1\n", Tickets);
Tickets--;
printf("卖出一张 还剩%d张\n", Tickets);
}
return 0;
}
DWORD WINAPI MyThreadFun_2(LPVOID pParameter) {
while (Tickets > 0) {
printf("当前票还剩余%d张 Thread-2 \n", Tickets);
Tickets--;
printf("卖出一张 还剩%d张\n", Tickets);
}
return 0;
}
int main() {
HANDLE hThreadArr[2];
hThreadArr[0] = CreateThread(
NULL, //获取默认的安全描述符,当前用户的令牌权限
0, //使用可执行文件的默认大小
MyThreadFun_1, // 创建线程调用的函数
NULL, // 传递函数中的参数
0, //线程在创建后立即运行
NULL // 不返回线程标识符
);
hThreadArr[1] = CreateThread(
NULL, //获取默认的安全描述符,当前用户的令牌权限
0, //使用可执行文件的默认大小
MyThreadFun_2, // 创建线程调用的函数
NULL, // 传递函数中的参数
0, //线程在创建后立即运行
NULL // 不返回线程标识符
);
// 当线程执行完毕之后,恢复阻塞 ,该函数具有局限性 只能等待单个线程执行完毕的情况
WaitForMultipleObjects(2, hThreadArr,true, INFINITE);
// 线程被清理的两个必要条件:1、线程内核对象的计数器为0 2、线程的执行代码执行完毕 ,这里的话只有线程中执行完才会进行CloseHandle
CloseHandle(hThreadArr[0]);
CloseHandle(hThreadArr[1]);
getchar();
return 0;
}
线程安全之互斥锁
CRITICAL_SECTION 线程锁结构体
InitializeCriticalSection 初始化线程锁结构体
EnterCriticalSection 固定锁
LeaveCriticalSection 释放锁
实现代码如下所示
#include<windows.h>
#include<stdio.h>
int Tickets = 10;
CRITICAL_SECTION myLock;
DWORD WINAPI MyThreadFun_1(LPVOID pParameter) {
while (true){
EnterCriticalSection(&myLock);
if (Tickets == 0) {
break;
}
printf("当前票还剩余%d张 Thread-1\n", Tickets);
Tickets--;
printf("卖出一张 还剩%d张\n", Tickets);
LeaveCriticalSection(&myLock);
}
return 0;
}
int main() {
InitializeCriticalSection(&myLock);
HANDLE hThreadArr[2];
hThreadArr[0] = CreateThread(
NULL, //获取默认的安全描述符,当前用户的令牌权限
0, //使用可执行文件的默认大小
MyThreadFun_1, // 创建线程调用的函数
NULL, // 传递函数中的参数
0, //线程在创建后立即运行
NULL // 不返回线程标识符
);
hThreadArr[1] = CreateThread(
NULL, //获取默认的安全描述符,当前用户的令牌权限
0, //使用可执行文件的默认大小
MyThreadFun_1, // 创建线程调用的函数
NULL, // 传递函数中的参数
0, //线程在创建后立即运行
NULL // 不返回线程标识符
);
// 当线程执行完毕之后,恢复阻塞 ,该函数具有局限性 只能等待单个线程执行完毕的情况
WaitForMultipleObjects(2, hThreadArr,true, INFINITE);
// 线程被清理的两个必要条件:1、线程内核对象的计数器为0 2、线程的执行代码执行完毕 ,这里的话只有线程中执行完才会进行CloseHandle
CloseHandle(hThreadArr[0]);
CloseHandle(hThreadArr[1]);
getchar();
return 0;
}
线程安全之互斥体
互斥体利用的是Windows互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问,在线程同步与保证程序单体运行上都有相当大的用处。
特点:互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享。
单线程互斥
#include<Windows.h>
#include<stdio.h>
DWORD WINAPI MyThreadFun(LPVOID pParameter) {
for (int i = 100; i > 0; i--) {
printf("%d---MyThreadFun\n", i);
Sleep(1000);
}
return 0;
}
int main() {
HANDLE mutex;
mutex = CreateMutex(NULL, false, "MyMutex"); //创建互斥体,名称为MyMutex
WaitForSingleObject(mutex, INFINITE); //等待获得互斥体
for (int i = 0; i < 10; i++) { //进行
printf("A进程----%d \n", i);
Sleep(1000);
}
ReleaseMutex(mutex);//释放互斥体
getchar();
return 0;
}
多线程互斥
#include<Windows.h>
#include<stdio.h>
HANDLE mutex;
int money = 100;
DWORD WINAPI MyThreadFun_1(LPVOID pParameter) {
while (money > 0){
WaitForSingleObject(mutex, INFINITE); //等待获取互斥对象
if (money == 0) {
break;
}
money--;
printf("%d Thread-1\n", money);
Sleep(50);
ReleaseMutex(mutex);//释放互斥体
}
return 0;
}
DWORD WINAPI MyThreadFun_2(LPVOID pParameter) {
while (money > 0) {
WaitForSingleObject(mutex, INFINITE); //等待获取互斥对象
if (money == 0) {
break;
}
money--;
printf("%d Thread-2\n", money);
Sleep(50);
ReleaseMutex(mutex);//释放互斥体
}
return 0;
}
int main() {
HANDLE aThread[2];
mutex = CreateMutex(NULL, false, "MyMutex"); //创建互斥体,名称为MyMutex
aThread[0]= CreateThread(
NULL, //获取默认的安全描述符,当前用户的令牌权限
0, //使用可执行文件的默认大小
MyThreadFun_1, // 创建线程调用的函数
NULL, // 传递函数中的参数
0, //线程在创建后立即运行
NULL // 不返回线程标识符
);
aThread[1] = CreateThread(
NULL, //获取默认的安全描述符,当前用户的令牌权限
0, //使用可执行文件的默认大小
MyThreadFun_2, // 创建线程调用的函数
NULL, // 传递函数中的参数
0, //线程在创建后立即运行
NULL // 不返回线程标识符
);
getchar();
CloseHandle(aThread[0]);
CloseHandle(aThread[1]);
return 0;
}
线程锁与互斥体的区别:
1、线程锁只能用于单个进程内部的线程控制,互斥体能用于单个进程,也可以多个进程
2、互斥体可以设定等待超时,但线程锁不能
3、线程意外终结时,Mutex可以避免无限等待,体现的地方比如意外exit了,而用互斥体实现线程同步,另一个进程中互斥体的句柄进行的线程还是会继续下去,不会一直进行等待
4、Mutex效率没有线程锁高
线程安全之事件
自己还是有点不太理解
#include<Windows.h>
#include<stdio.h>
int money = 100;
HANDLE eve;
/*
HANDLE WINAPI CreateEvent(
__in LPSECURITY_ATTRIBUTES lpEventAttributes, //事件对象的安全属性,如果为Null不能被子进程继承If this parameter is NULL, the handle cannot be inherited by child processes
__in BOOL bManualReset,//事件对象的类型,TRUE表示人工重置事件对象,FLASE表示自动重置事件对象;
__in BOOL bInitialState,//事件初始状态 true通知状态;flase 未通知状态
__in LPCTSTR lpName//事件对象的名称If lpName is NULL, the event object is created without a name
);
*/
DWORD WINAPI MyThreadFun_1(LPVOID pParameter) {
while (money > 0){
WaitForSingleObject(eve, INFINITE); // 等待,直到事件对象收到信号
money--;
Sleep(50);
printf("%d Thread-1\n", money);
SetEvent(eve); // 释放事件对象,并且把当前线程挂起,再发信号唤醒给另外的线程
}
return 0;
}
DWORD WINAPI MyThreadFun_2(LPVOID pParameter) {
while (money > 0) {
WaitForSingleObject(eve, INFINITE);// 等待,直到事件对象收到信号
money--;
Sleep(50);
printf("%d Thread-2\n", money);
SetEvent(eve); //释放事件对象,并且把当前线程挂起,再发信号唤醒给另外的线程
}
return 0;
}
int main() {
HANDLE arrThread[2];
eve = CreateEvent(NULL, false,true, NULL);
// CreateEvent 第二个参数可以理解为 是否为通知类型,当设置为true的时候则为通知类型 线程都执行 ,false则为互斥 只有一个线程可以执行
//第三个参数 为 初始有无信号,若无信号 则需要我们手动SetEvent
arrThread[0]= CreateThread(
NULL, //获取默认的安全描述符,当前用户的令牌权限
0, //使用可执行文件的默认大小
MyThreadFun_1, // 创建线程调用的函数
NULL, // 传递函数中的参数
0, //线程在创建后立即运行
NULL // 不返回线程标识符
);
arrThread[1] = CreateThread(
NULL, //获取默认的安全描述符,当前用户的令牌权限
0, //使用可执行文件的默认大小
MyThreadFun_2, // 创建线程调用的函数
NULL, // 传递函数中的参数
0, //线程在创建后立即运行
NULL // 不返回线程标识符
);
//SetEvent(eve); //设置为有信号,如果这里写了的话,那么CreateEvent的第三个参数需要设置为false
WaitForMultipleObjects(2, arrThread, true, INFINITE); //等待多线程执行完成
CloseHandle(arrThread[0]);
CloseHandle(arrThread[1]);
CloseHandle(eve); //关闭内核对象
getchar();
return 0;
}