线程安全问题: 当多个线程对同一个全局变量进行操作,并且进行写的操作的时候所引发的问题
存在线程安全问题的代码:
#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效率没有线程锁高
原文:https://www.cnblogs.com/zpchcbd/p/12249758.html