线程互斥

前言:线程互斥的学习笔记,今天重新看了下这篇笔记发现很多需要缺漏的,今天重新写一篇

线程安全问题:当多个线程对同一个全局变量(临界资源)进行操作,并且同时进行读写的操作的时候所引发的数据不统一问题

需要提前知道的几个学术上的概念

临界资源:临界资源是一次仅允许一个进程使用的资源称为临界资源

临界区:访问临界资源的代码区称为临界区

存在线程安全问题的代码

#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;
}
posted @ 2020-02-01 19:39  zpchcbd  阅读(237)  评论(0编辑  收藏  举报