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

news2024/10/6 2:53:20
  • 公开视频 -> 链接点击跳转公开课程
  • 博客首页 -> 链接点击跳转博客主页

目录

竞态条件

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.coloradmin.cn/o/1903877.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

02STM32软件安装新建工程

STM32软件安装&新建工程 1.软件安装&#xff1a;1.1Keil5 MDK安装1.2安装器件支持包离线安装支持包在线安装支持包 1.3软件注册&#xff1a;1.4安装驱动STLINK驱动JLink驱动在此文件夹下USB转串口 2开发方式&新建工程步骤&架构个人心得 1.软件安装&#xff1a; 安…

stm32精密控制步进电机(基础篇)

众所周知&#xff0c;步进电机由于使用脉冲控制&#xff0c;会比直流电机的控制稍难一些&#xff0c;但开环控制时也更加稳定。 落到做项目的时候&#xff0c;目前来说我都会先考虑步进电机&#xff0c;再去考虑直流&#xff0c;无刷这样的电机。包括毕设时所用的机械臂也是用…

Linux开发讲课33---线程实现与线程控制步骤简析

线程概述 进程是系统中程序执行和资源分配的基本单位。 每个进程都拥有自己的数据段、代码段和堆栈段&#xff0c;这就造成了进程在进行切换等操作时都需要有比较负责的上下文切换等动作。为了进一步减少处理机的空转时间支持多处理器和减少上下文切换开销&#xff0c;进程在演…

Java基础-内部类与异常处理

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 一、Java 内部类 什么是内部类&#xff1f; 使用内部类的优点 访问局部变量的限制 内部类和继承 内部…

一位3D打印工程师繁忙的一天

早晨&#xff1a;准备与规划 7:00 AM - 起床与晨练 3D打印工程师早起进行晨练&#xff0c;如跑步或瑜伽&#xff0c;以保持身心健康。晨练后&#xff0c;享用一顿健康的早餐&#xff0c;为一天的繁忙工作做好准备。 8:00 AM - 检查邮件和项目进展 在开始工作前&#xff0c;先…

封装了一个仿照抖音效果的iOS评论弹窗

需求背景 开发一个类似抖音评论弹窗交互效果的弹窗&#xff0c;支持滑动消失&#xff0c; 滑动查看评论 效果如下图 思路 创建一个视图&#xff0c;该视图上面放置一个tableView, 该视图上添加一个滑动手势&#xff0c;同时设置代理&#xff0c;实现代理方法 (BOOL)gestur…

Zkeys三方登录模块支持QQ、支付宝登录

1&#xff0c;覆盖到根目录&#xff0c;并导入update.sql数据库文件到Zkeys数据库里 2. 后台系统权限管理&#xff0c;配置管理员权限-系统类别-找到云外科技&#xff0c;全部打勾 3&#xff0c;后台系统设置找到云外快捷登录模块填写相应的插件授权配置和登录权限配置&#x…

【python中级】图像从从笛卡尔坐标系转换为极坐标系

【python中级】图像从从笛卡尔坐标系转换为极坐标系 1.背景2.生成二维图3.极坐标转换1.背景 笛卡尔坐标系就是我们常说的直角坐标系。 笛卡尔坐标系,也称为直角坐标系,是由法国数学家和哲学家勒内笛卡尔(Ren Descartes)发明的一种二维或三维坐标系统。它使用两个或三个相互…

人工智能在病理切片虚拟染色及染色标准化领域的系统进展分析|文献速递·24-07-07

小罗碎碎念 本期文献主题&#xff1a;人工智能在病理切片虚拟染色及染色标准化领域的系统进展分析 这一期文献的速递&#xff0c;是有史以来数量最大的一次&#xff0c;足足有十一篇&#xff0c;本来打算分两期写&#xff0c;但是为了知识的系统性&#xff0c;我决定咬咬牙&…

texStudio使用(小白)

原先使用overleaf在线编译&#xff0c;可能eps格式的图片太大导致需要充钱&#xff0c;所以考虑本地安装 安装教程参考B站视频&#xff1a;B站Latex本地编译器安装&#xff1a;TexLive TextStudio 踩到坑&#xff1a; 1. 编译器位置要选择对 因为BibTex选成了Biber导致出现无…

minist数据集分类模型的训练

minist数据集训练 训练方法&#xff1a;利用pytorch来实现minist数据集的分类模型训练 训练模型如下图所示 模型代码&#xff1a; import torch from torch import nn from torch.nn import Flattenclass Net(nn.Module):def __init__(self):super().__init__()self.module …

文件管理下:文件函数的学习

前言 Hello,小伙伴们你们的作者君又来了&#xff0c;上次我们简单介绍了文件的坐拥并简单提到了数据的读取&#xff0c;和C语言的默认流的作用&#xff0c;今天我将继续带领大家探索文件的奥秘&#xff0c;大家准别好了吗&#xff1f; 在内容开始之前还是按照惯例&#xff0c…

**kwargs 字典解包传参的方式

字典解包传参 在Python中&#xff0c;****kwargs**是一种通过字典解包 (dictionary unpacking) 的方式进行参数传递的方式。它将一个字典的键值对解包并传递给函数的命名参数。 示例代码 kwargs实参: {name: "jordan", age: 18, score: [80, 85, 85]} get_info形…

下载linux的吐槽

本来这几天放假了&#xff0c;想下一个linux玩一玩 教程&#xff08;我就是根据这个教程进行下载的&#xff0c;但是呢在进行修改BIOS 模式的 地方遇见了困难&#xff0c;也许是电脑修过的原因&#xff0c;我狂按F12 以及 FnF12都没有BIOS设置&#xff0c;只有一个让我选择用w…

前端扫盲:cookie、localStorage和sessionStorage

cookie、localStorage和sessionStorage都是存储数据的方式&#xff0c;他们之间有什么不同&#xff0c;各有什么应用场景&#xff0c;本文为您一一解答。 一、什么是cookie、localStorage和sessionStorage 1. Cookie是一种存储在用户计算机上的小型文本文件&#xff0c;由服务…

子数组按位与为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 > …

通过消息传递同步操作

通信顺序进程&#xff08;CSP&#xff09; 是一种形式语言&#xff0c;用来描述并发性系统间进行交互的模式 每个线程或进程独立运行&#xff0c;它们之间仅通过消息传递进行通信&#xff0c;而不是直接共享状态 每个线程实际上都是一个状态机&#xff1a;当它接收到一条消息时…

Zabbix监控软件

目录 一、什么是Zabbix 二、zabbix监控原理 三、zabbix 安装步骤 一、什么是Zabbix ●zabbix 是一个基于 Web 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。 ●zabbix 能监视各种网络参数&#xff0c;保证服务器系统的安全运营&#xff1b;并提供灵活的…

基于LoFTR_TRT项目实现LoFTR模型的trt推理与onnx推理,3060显卡下320图像30ms一组图

本博文主要记录了使用LoFTR_TRT项目将LoFTR模型导出为onnx模型&#xff0c;然后将onnx模型转化为trt模型。并分析了LoFTR_TRT与LoFTR的基本代码差异&#xff0c;但从最后图片效果来看是与官网demo基本一致的&#xff0c;具体可以查看上一篇博客记录。最后记录了onnx模型的使用【…

深入探索 Python 中的数据维数:高维数据处理方法与应用

Python 数据维数 在数据科学和机器学习领域&#xff0c;理解数据的维度是至关重要的。Python作为一种强大而灵活的编程语言&#xff0c;提供了丰富的工具和库来处理各种维度的数据。本文将介绍Python中数据维数的概念&#xff0c;以及如何使用Python库来处理不同维度的数据。 什…