WIN32核心编程 - 线程操作(二) 同步互斥

  • 公开视频 -> 链接点击跳转公开课程
  • 博客首页 -> 链接点击跳转博客主页

目录

竞态条件

CriticalSection

Mutex

CriticalSection & Mutex

Semaphore

Event


竞态条件

  • 多线程环境下,当多个线程同时访问或者修改同一个数据时,最终结果为线程执行的时许。

  • 如果没有同步机制,会发生竞态条件,可能导致数据不准确,或者程序发生异常等。

#include <iostream>
#include <windows.h>

DWORD g_Num = 0;

DWORD WINAPI WorkThread(LPVOID lp)
{
    for (size_t i = 0; i < 10000000; i++)
    {
        //g_Num++;
        __asm LOCK INC [g_Num] 
    }
    return 0;
}

int main()
{
    HANDLE hThread[2] = { 0 };
    hThread[0] = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
    hThread[1] = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
    WaitForMultipleObjects(2, hThread, TRUE, -1);
    std::cout << g_Num << std::endl;

    return 0;
}

CriticalSection

#include <iostream>
#include <windows.h>

DWORD g_Num = 0;
CRITICAL_SECTION cs = { 0 };

DWORD WINAPI WorkThread(LPVOID lp)
{
	for (size_t i = 0; i < 1000000; i++)
	{
		// 进入临界区
		EnterCriticalSection(&cs);

		// TODO
		g_Num++;

		// 退出临界区
		LeaveCriticalSection(&cs);
	}
	return 0;
}

int main()
{

	HANDLE hThread[2] = { 0 };
	// 初始临界区
	InitializeCriticalSection(&cs);

	hThread[0] = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
	hThread[1] = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);

	WaitForMultipleObjects(2, hThread, TRUE, -1);

	std::cout << g_Num << std::endl;

	// 清理临界区
	DeleteCriticalSection(&cs);

	return 0;
}

Mutex

  • 互斥体(Mutex),用于防止多个线程同时访问或修改共享资源。

  • 同一时刻下只有一个线程可以拥有互斥体的所有权,如果一个线程拥有了互斥体的所有权,则其他请求该互斥体的线程将会被阻塞,直到互斥体权限释放。

  • 创建互斥体 - CreateMutex

  • 请求互斥体 - WaitForSingleObject

  • 释放互斥体 - ReleaseMutex

#include <iostream>
#include <windows.h>

HANDLE hMutex = 0;
DWORD g_Num = 0;

DWORD WINAPI WorkThread(LPVOID lp)
{
	for (size_t i = 0; i < 100000; i++)
	{
		WaitForSingleObject(hMutex, INFINITE);
		g_Num++;
		ReleaseMutex(hMutex);
	}

	return 0;
}

int main()
{
	hMutex = CreateMutex(NULL, FALSE, NULL); 

	HANDLE hThread1 = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
	HANDLE hThread2 = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);

	WaitForSingleObject(hThread1, INFINITE);
	WaitForSingleObject(hThread2, INFINITE);

	CloseHandle(hThread1);
	CloseHandle(hThread2);
	CloseHandle(hMutex);
	
	std::cout << g_Num << std::endl;

	return 0;
}

CriticalSection & Mutex

  • 临界区

    • 共享资源的线程同步机制,临界区在同一进程的线程之间提供了互斥访问。

    • 每个线程在访问共享资源之前必须先能够进入临界区,在访问结束后离开临界区,完成线程同步。

  • 互斥体

    • 线程同步机制,用来限制多个线程同时访问共享资源。

    • 互斥体可以同步进程或者线程,且可以跨进程同步。
    • #include <iostream>
      #include <Windows.h>
      
      int main()
      {
      	HANDLE hMutex = CreateMutex(NULL, FALSE, L"0xCC_Mutex");
      	if (hMutex == NULL) return 0;
      	if (GetLastError() == ERROR_ALREADY_EXISTS)
      	{
      		MessageBox(NULL, L"禁止多开", L"错误", MB_OKCANCEL);
      		return 0;
      	}
      	std::cout << "Game Start..." << std::endl;
      	system("pause");
      	CloseHandle(hMutex);
      	return 0;
      }
  • 性能

    • 临界区在同一进程的线程中比互斥体更快。

  • 功能

    • 互斥体可以跨进程同步,但临界区只能够在同一个进程下的线程之间进行同步。

  • 所有权

    • 互斥体有严格的所有权要求,只有拥有互斥体权限的线程才能够释放它。

  • 死锁
    • 当线程在持有锁的情况下意外死亡(异常)
    • 如果线程在持有临界区锁的情况下意外终结,这个锁不会被释放,导致其他等待该临界区的线程无法正常执行,造成死锁。
    • #include <iostream>
      #include <Windows.h>
      
      CRITICAL_SECTION CriticalSection = { 0 };
      
      DWORD WINAPI WorkThread(LPVOID lp)
      {
      	EnterCriticalSection(&CriticalSection);
      	
      	printf("TID -> %d \r\n", GetCurrentThreadId());
      	Sleep(5000);
      
      	LeaveCriticalSection(&CriticalSection);
      
      	return 0;
      }
      
      int main()
      {
      	InitializeCriticalSection(&CriticalSection);
      
      	HANDLE hThread1 = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
      	Sleep(1000);
      	TerminateThread(hThread1, 1);
      
      	HANDLE hThread2 = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
      	WaitForSingleObject(hThread2, INFINITE);
      
      	DeleteCriticalSection(&CriticalSection);
      
      	return 0;
      }
    • 如果线程在持有互斥体锁的情况下意外终结,Windows会自动释放其所有权,使得其他线程可以继续正常执行。
    • #include <iostream>
      #include <Windows.h>
      
      HANDLE hMutex = NULL;
      
      DWORD WINAPI WorkThread1(LPVOID lp)
      {
      	WaitForSingleObject(hMutex, INFINITE);
      	printf("TID -> %d \r\n", GetCurrentThreadId());
      	Sleep(5000);
      	TerminateThread(GetCurrentThread(), -1);
      
      	//todo
      	return 0;
      }
      
      DWORD WINAPI WorkThread2(LPVOID lp)
      {
      	printf("Wait For Thread1 Leave\r\n");
      	WaitForSingleObject(hMutex, INFINITE);
      	printf("TID -> %d \r\n", GetCurrentThreadId());
      	ReleaseMutex(hMutex);
      	return 0;
      }
      
      
      int main()
      {
      	hMutex = CreateMutex(NULL, FALSE, NULL);
      
      	HANDLE hThread1 = CreateThread(NULL, 0, WorkThread1, NULL, 0, NULL);
      	Sleep(1000);
      
      	HANDLE hThread2 = CreateThread(NULL, 0, WorkThread2, NULL, 0, NULL);
      	WaitForSingleObject(hThread2, INFINITE);
      
      	CloseHandle(hMutex);
      	CloseHandle(hThread1);
      	CloseHandle(hThread2);
      
      	return 0;
      }

Semaphore

  • 信号量是一种同步对象,用于控制多个线程对共享资源的访问。它是一个计数器,用来表示可用资源的数量。当信号量的值大于0,它表示有资源可用;当值为0,表示没有可用资源。

    • 等待:试图减少信号量的值。如果信号量的值大于0,减1并继续执行。如果信号量的值为0,则线程阻塞,直到信号量的值变为大于0。

    • 释放:增加信号量的值。如果有其他线程因等待这个信号量而阻塞,它们中的一个将被唤醒。

  • 创建信号量

    • 在 Windows 系统中,使用 CreateSemaphoreCreateSemaphoreEx 函数创建信号量。

  • 等待(Wait)和释放(Release)信号量

    • 等待信号量通常使用 WaitForSingleObjectWaitForMultipleObjects 函数。

    • 释放信号量使用 ReleaseSemaphore 函数。

#include <iostream>
#include <Windows.h>

#define MAX_COUNT_SEMAPHORE 3


HANDLE g_SemapHore = NULL;
HANDLE g_hThreadArr[10] = { 0 };

DWORD WINAPI WorkThread(LPVOID lp)
{
	WaitForSingleObject(g_SemapHore, INFINITE);

	for (size_t i = 0; i < 10; i++)
	{
		std::cout << "COUNT -> " << (int)lp << std::endl;
		Sleep(500);
	}

	ReleaseSemaphore(g_SemapHore, 1, NULL);

	return 0;
}

int main()
{
	g_SemapHore = CreateSemaphore(
		NULL,						//安全属性
		MAX_COUNT_SEMAPHORE,		//初始计数
		MAX_COUNT_SEMAPHORE,		//最大计数
		NULL						//信号名称
	);
	
	if (g_SemapHore == NULL)
	{
		std::cout << GetLastError() << std::endl;
		return 1;
	}

	for (size_t i = 0; i < 10; i++)
	{
		g_hThreadArr[i] = CreateThread(
			NULL,
			0,
			WorkThread,
			(LPVOID)i,
			0,
			NULL
		);
	}
	
	WaitForMultipleObjects(10, g_hThreadArr, TRUE, INFINITE);
	
	//closehandle

	return 0;
}

Event

  • 在Windows编程中,事件是一种同步机制,用于在多个线程之间发送信号。事件对象可以是手动重置自动重置

    • 手动重置事件(Manual Reset Event):当事件被设置(signaled)后,它将保持这个状态直到显式地被重置。这意味着多个等待该事件的线程都可以在事件被重置之前被唤醒。

    • 自动重置事件(Auto Reset Event):当事件被一个等待的线程接收(signaled)后,系统会自动将事件状态重置为非信号状态(non-signaled)。这意味着每次只允许一个线程被唤醒。

  • 创建事件

    • 使用Windows API函数CreateEvent可以创建一个事件对象

    • lpEventAttributes:指向安全属性的指针,如果设置为NULL,则使用默认安全性。

    • bManualReset:如果为TRUE,则创建一个手动重置事件,否则创建自动重置事件。

    • bInitialState:如果为TRUE,则初始状态为信号状态;如果为FALSE,则为非信号状态。

    • lpName:事件的名称。

  • 设置事件(将事件状态设置为信号状态)使用SetEvent函数

  • 重置事件(将事件状态设置为非信号状态)使用ResetEvent函数

  • 等待事件 等待一个事件对象变为信号状态使用WaitForSingleObject函数

#include <iostream>
#include <Windows.h>

DWORD WINAPI WorkThread(LPVOID lp)
{
	HANDLE hEvent = *(HANDLE*)lp;
	std::cout << "Thread - " << GetCurrentThreadId() << " Waiting For Event" << std::endl;
	WaitForSingleObject(hEvent, INFINITE);
	std::cout << "Thread - " << GetCurrentThreadId() << " actived" << std::endl;
	return 0;
}

int main()
{
	HANDLE hThreads[3] = { 0 };
	HANDLE hEvent = NULL;

	hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (hEvent == NULL) return 0;

	for (size_t i = 0; i < 3; i++)
	{
		hThreads[i] = CreateThread(NULL, 0, WorkThread, &hEvent, 0, NULL);
	}

	Sleep(2000);
	SetEvent(hEvent);
	
	WaitForMultipleObjects(3, hThreads, TRUE, INFINITE);

	CloseHandle(hEvent);

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/780656.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java】垃圾回收学习笔记(二):分代假说与垃圾回收算法

文章目录 0. 分代收集理论分代假说分代GC定义 1. 垃圾回收算法1.1 标记清除&#xff08;Mark-Sweep&#xff09;算法优点缺点 1.2 标记复制算法优点缺点为什么是8:1:1&#xff1f; 1.3 标记整理算法优点缺点 2. 是否移动&#xff1f; 0. 分代收集理论 分代假说 现在多数JVM G…

子数组按位与为k

注意这里的子数组是连续的非空的数组&#xff0c;我们要学会与处理就是求交集 class Solution { public:long long countSubarrays(vector<int>& nums, int k) {long long ans 0;for (int i 0; i < nums.size(); i) {int x nums[i];for (int j i - 1; j > …

MATLAB 2024b 更新了些什么?

MATLAB 2024b版本已经推出了预览版&#xff0c;本期介绍一些MATLAB部分的主要的更新内容。 帮助浏览器被移除 在此前的版本&#xff0c;当我们从MATLAB中访问帮助文档时&#xff0c;默认会通过MATLAB的帮助浏览器&#xff08;Help browser&#xff09;。 2024b版本开始&…

【Linux】压缩命令——gzip,bzip2,xz

1.压缩文件的用途与技术 你是否有过文件太大&#xff0c;导致无法以正常的E-mail方式发送&#xff1f;又或学校、厂商要求使用CD或DVD来做数据归档之用&#xff0c;但是你的单一文件却都比这些传统的一次性存储媒介还要大&#xff0c;那怎么分成多块来刻录&#xff1f;还有&am…

[搭建个人网站] 云服务器 or 本地环境搭建

别人没有的&#xff0c;你有&#xff0c;你就牛。 面试&#xff0c;吹牛皮的时候 都可以拉出来溜溜 本文介绍2种搭建网站模式方式&#xff1a;区别嘛&#xff08;花钱跟不花钱&#xff09; 花钱&#xff1a; 1. 先购买个域名。。&#xff08;这里就不多介绍了&#xff0c;随便…

昇思学习打卡-9-ResNet50图像分类

文章目录 网络介绍数据可视化部分网络实现Building Block结构BottleNet模块 模型训练推理结果可视化学习总结优点不足 网络介绍 在ResNet网络提出之前&#xff0c;传统的卷积神经网络都是将一系列的卷积层和池化层堆叠得到的&#xff0c;但当网络堆叠到一定深度时&#xff0c;…

STM32崩溃问题排查

文章目录 前言1. 问题说明2. STM32&#xff08;Cortex M4内核&#xff09;的寄存器3. 崩溃问题分析3.1 崩溃信息的来源是哪里&#xff1f;3.2 崩溃信息中的每个关键字代表的含义3.3 利用崩溃信息去查找造成崩溃的点3.4 keil5中怎么根据地址找到问题点3.5 keil5上编译时怎么输出…

C++模板元编程(二)——完美转发

完美转发指的是函数模板可以将自己的参数“完美”地转发给内部调用的其它函数。所谓完美&#xff0c;即不仅能准确地转发参数的值&#xff0c;还能保证被转发参数的左、右值属性不变。 文章目录 场景旧的方法新的方法内部实现参考文献 场景 思考下面的代码&#xff1a; templ…

深度学习之网络构建

目标 选择合适的神经网络 卷积神经网络&#xff08;CNN&#xff09;&#xff1a;我们处理图片、视频一般选择CNN 循环神经网络&#xff08;RNN&#xff09;&#xff1a;我们处理时序数据一般选择RNN 超参数的设置 为什么训练的模型的错误率居高不下 如何调测出最优的超参数 …

【pytorch20】多分类问题

网络结构以及示例 该网络的输出不是一层或两层的&#xff0c;而是一个十层的代表有十分类 新建三个线性层&#xff0c;每个线性层都有w和b的tensor 首先输入维度是784&#xff0c;第一个维度是ch_out,第二个维度才是ch_in(由于后面要转置)&#xff0c;没有经过softmax函数和…

C++ 引用——引用的本质

本质&#xff1a;引用的本质在c内部实现是一个指针常量 C推荐用引用技术&#xff0c;因为语法方便&#xff0c;引用本质是指针常量&#xff0c;但是所有的指针操作编译器都帮我们做了 示例&#xff1a; 运行结果&#xff1a;

C++初学者指南-4.诊断---valgrind

C初学者指南-4.诊断—Valgrind Valgrind&#xff08;内存错误检测工具&#xff09; 检测常见运行时错误 读/写释放的内存或不正确的堆栈区域使用未初始化的值不正确的内存释放&#xff0c;如双重释放滥用内存分配函数内存泄漏–非故意的内存消耗通常与程序逻辑缺陷有关&#xf…

水箱高低水位浮球液位开关工作原理

工作原理 水箱高低水位浮球液位开关是一种利用浮球随液位升降来实现液位控制的设备。其基本原理是浮球在液体的浮力作用下上下浮动&#xff0c;通过磁性作用驱动与之相连的磁簧开关的开合&#xff0c;从而实现液位的高低控制和报警。当液位升高时&#xff0c;浮球上浮&#xf…

cmake find_package 使用笔记

目录 1 find_package2 config mode2.1 搜索的文件名2.2 搜索路径 3 module mode3.1 搜索的文件名3.2 搜索路径 参考 1 find_package 这是官方文档 下面是学习总结&#xff1a; 首先是find_package的作用是什么&#xff1f;引入预编译的库。 find_package有两种模式&#xff1a…

C语言 指针和数组——指针和二维数组之间的关系

目录 换个角度看二维数组 指向二维数组的行指针 按行指针访问二维数组元素 再换一个角度看二维数组 按列指针访问二维数组元素 二维数组作函数参数 指向二维数组的行指针作函数参数 指向二维数组的列指针作函数参数​编辑 用const保护你传给函数的数据 小结 换个角度看…

使用antd的<Form/>组件获取富文本编辑器输入的数据

前端开发中&#xff0c;嵌入富文本编辑器时&#xff0c;可以通过富文本编辑器自身的事件处理函数将数据传输给后端。有时候&#xff0c;场景稍微复杂点&#xff0c;比如一个输入页面除了要保存富文本编辑器的内容到后端&#xff0c;可能还有一些其他输入组件获取到的数据也一并…

Win10安装MongoDB(详细版)

文章目录 1、安装MongoDB Server1.1. 下载1.2. 安装 2、手动安装MongoDB Compass(GUI可视工具)2.1. 下载2.2.安装 3、测试连接3.1.MongoDB Compass 连接3.2.使用Navicat连接 1、安装MongoDB Server 1.1. 下载 官网下载地址 https://www.mongodb.com/try/download/community …

『大模型笔记』《Pytorch实用教程》(第二版)

『大模型笔记』《Pytorch实用教程》(第二版) 文章目录 一. 《Pytorch实用教程》(第二版)1.1 上篇1.2 中篇1.3 下篇1.4 本书亮点1.5 本书内容及结构二. 参考文献🖥️ 配套代码(开源免费):https://github.com/TingsongYu/PyTorch-Tutorial-2nd📚 在线阅读(开源免费)…

nginx相关概念(反向代理、负载均衡)

1 Nginx 是什么 Nginx是一款轻量级的Web 服务器&#xff0c;其特点是占有内存少&#xff0c;并发能力强 2 Nginx 反向代理 正向代理代替客户端去发送请求反向代理代替服务端接受请求 2.1 正向代理 若客户端无法直接访问到目标服务器 server 则客户端需要配置代理服务器 pr…

正则表达式 先行断言 \S {} 示例

目录 数据准备一. 先行断言1.1 正向先行断言1.2 负向先行断言 二. 配合 {} 和 \S 使用2.1 匹配一个任意非空白字符2.2 匹配任意多个非空白字符2.3 匹配3个非空白字符2.4 匹配至少3个非空白字符2.5 匹配0~3个非空白字符 数据准备 ⏹文件1 0561-10 AAA 123 dfg 345 sss 0561-2…