首页 > 编程语言 >Win32编程之线程池同步(十三)

Win32编程之线程池同步(十三)

时间:2023-09-19 17:12:03浏览次数:54  
标签:函数 编程 Win32 临界 线程 gCriticalSection PTP LeaveCriticalSection

1.InterlockedAdd函数

InterlockedAdd 是 Windows API 中的一个原子操作函数,用于在多线程环境下对一个变量执行原子加法操作。原子操作是指在执行期间不会被其他线程中断,从而确保多线程环境下的数据一致性。

函数原型:

LONG InterlockedAdd(
  LONG volatile *Addend,
  LONG Value
);

参数解释:

  • Addend:一个指向被加数的 LONG 类型指针。这是要修改的变量的地址。
  • Value:要加到变量上的值。

返回值:

InterlockedAdd 函数返回 Addend 参数原始值的拷贝,即执行加法操作前的值。

函数功能:

InterlockedAdd 函数的作用是将 Addend 指针指向的变量的值与 Value 相加,并将结果存储在该变量中。这个操作是原子的,意味着在执行加法操作的过程中,不会被其他线程中断,从而确保多线程环境下的数据一致性。它适用于实现各种线程间的计数和累加操作。

示例代码:

#include <Windows.h>
#include <stdio.h>

LONG g_count = 0;

void CALLBACK ThreadFunc(PTP_CALLBACK_INSTANCE pInstance, void* p, PTP_WORK pWork)
{
	for (int i = 0; i < 200; i++) {
		InterlockedAdd(&g_count, 1);
	}
}

int main() {
	PTP_WORK pwk = CreateThreadpoolWork((PTP_WORK_CALLBACK)ThreadFunc, NULL, NULL);
	for (int i = 0; i < 100000; i++) {
		SubmitThreadpoolWork(pwk);
	}

	WaitForThreadpoolWorkCallbacks(pwk, FALSE);

	CloseThreadpoolWork(pwk);

	printf("g_count = %d\n", g_count);

	system("pause");

	return 1;
}

二、设置临界区

在 Windows 操作系统中,可以使用临界区(Critical Section)来实现线程间的互斥访问,确保多个线程不会同时访问共享资源,从而避免竞态条件和数据不一致性问题。 

以下是如何在 Windows 中定义和使用临界区的基本步骤:

步骤 1:定义临界区变量

首先,需要定义一个 CRITICAL_SECTION 结构体变量,用于表示临界区对象。通常,将其定义为全局或局部变量,以确保多个线程都可以访问。 

CRITICAL_SECTION gCriticalSection; // 全局临界区变量

步骤 2:初始化临界区

在使用临界区之前,必须对其进行初始化。可以使用 InitializeCriticalSection 函数进行初始化。

InitializeCriticalSection(&gCriticalSection);

步骤 3:进入临界区

要进入临界区(获取临界区锁),使用 EnterCriticalSection 函数。一旦线程进入临界区,其他线程将被阻塞,直到该线程退出临界区。

EnterCriticalSection(&gCriticalSection);

步骤 4:执行临界区代码

在进入临界区后,可以执行需要互斥访问的代码段,例如修改共享资源的操作。

步骤 5:离开临界区

要离开临界区(释放临界区锁),使用 LeaveCriticalSection 函数。

LeaveCriticalSection(&gCriticalSection);

步骤 6:销毁临界区

在不再需要使用临界区时,应该对其进行清理,释放相关资源。可以使用 DeleteCriticalSection 函数来销毁临界区对象。

DeleteCriticalSection(&gCriticalSection);

示例代码: 

#include <Windows.h>
#include <stdio.h>

LONG g_count = 0;

void CALLBACK ThreadFunc(PTP_CALLBACK_INSTANCE pInstance, void* p, PTP_WORK pWork)
{	
	EnterCriticalSection((CRITICAL_SECTION*)p);

	for (int i = 0; i < 200; i++) {
		InterlockedAdd(&g_count, 1);
	}

	//LeaveCriticalSection((CRITICAL_SECTION*)p);
	LeaveCriticalSectionWhenCallbackReturns(pInstance, (CRITICAL_SECTION*)p);
}

int main() {
	CRITICAL_SECTION cs;
	InitializeCriticalSection(&cs);

	PTP_WORK pwk = CreateThreadpoolWork((PTP_WORK_CALLBACK)ThreadFunc, &cs, NULL);
	for (int i = 0; i < 100000; i++) {
		SubmitThreadpoolWork(pwk);
	}

	WaitForThreadpoolWorkCallbacks(pwk, FALSE);
	DeleteCriticalSection(&cs);

	CloseThreadpoolWork(pwk);

	printf("g_count = %d\n", g_count);

	system("pause");

	return 1;
}

LeaveCriticalSection 函数和 LeaveCriticalSectionWhenCallbackReturns 函数都用于释放临界区锁(退出临界区),但它们的使用场景和行为略有不同:

LeaveCriticalSection 函数:

  • LeaveCriticalSection 函数用于在普通的代码路径中手动退出临界区。这意味着,在临界区中的任何线程都可以调用 LeaveCriticalSection 来释放临界区锁。

  • 调用 LeaveCriticalSection 会立即释放临界区锁,使得其他线程有机会进入该临界区。

  • 这个函数通常用于手动管理临界区的锁定和解锁操作。

示例用法:

EnterCriticalSection(&gCriticalSection);
// 执行需要互斥访问的操作
LeaveCriticalSection(&gCriticalSection);

LeaveCriticalSectionWhenCallbackReturns 函数:

  • LeaveCriticalSectionWhenCallbackReturns 函数通常用于在回调函数中退出临界区。回调函数是通过线程池或异步 I/O 操作注册的,当操作完成时会自动调用。

  • 这个函数的主要目的是确保在回调函数执行完毕后才离开临界区。这对于在回调函数中对共享资源进行安全访问非常有用。

  • 使用 LeaveCriticalSectionWhenCallbackReturns 可以确保在回调函数执行期间,其他线程不会进入相同的临界区。

示例用法(在回调函数中):

// 在异步操作的回调函数中
LeaveCriticalSectionWhenCallbackReturns(&gCriticalSection, callbackContext);

总之,LeaveCriticalSection 用于手动管理临界区的锁定和解锁,而 LeaveCriticalSectionWhenCallbackReturns 通常用于在回调函数中确保临界区锁的安全释放。它们的选择取决于你的代码逻辑和使用场景。    

标签:函数,编程,Win32,临界,线程,gCriticalSection,PTP,LeaveCriticalSection
From: https://www.cnblogs.com/TechNomad/p/17715168.html

相关文章

  • springboot线程池底的使用
    1.简单使用一、配置类@Configuration@EnableAsyncpublicclassSpringAsyncConfig{@Bean("taskExecutor")publicExecutorasyncServiceExecutor(){ThreadPoolTaskExecutorexecutor=newThreadPoolTaskExecutor();//设置核心线程数......
  • 线程劫持-进程注入C++示例和检测思考
    线程劫持:运行方法C:\Users\l00379637\source\repos\thread_hijack\x64\Release\thread_hijack.exe18132C:\Users\l00379637\source\repos\injected_dll\x64\Release\injected_dll.dllProcessID:18132Injected!劫持效果: 劫持代码如下:#include<iostream......
  • 湖南大学结对编程个人项目互评
    湖南大学结对编程个人项目互评此文是我对队友汤家贝的个人项目的评价 ♊目录  项目描述与分析  代码结构分析  代码功能测试  代码风格分析  总结 ♋项目描述与分析 ♌代码结构分析 ♍代码功能测试 ♎代码风格分析 ♏总结 ......
  • 编程基础
    类采用Class作为关键字进行定义的代码块,表示的是一种类别对象实例化之后的类,对类中的形参进行了赋值,赋予其真正的含义或数值方法使用def作为关键词,定义在类内的函数函数使用def作为关键词,但是没有在类内进行定义,即定义在类外属性类内的称呼,其实就是类内的变量,同一个类内......
  • 面向对象编程特征?
    面向对象编程(Object-OrientedProgramming,OOP)的特征包括以下几个方面:封装(Encapsulation):封装是将对象的状态(属性)和行为(方法)捆绑在一起,并对外部隐藏对象的内部细节。通过访问修饰符(如public、private、protected等)来限制对对象属性的直接访问,以确保数据的安全性和一致性。封......
  • 万字长文深度解读Java线程池,硬核源码分析
    前言本文将深入分析Java线程池的源码,包括线程池的创建、任务提交、工作线程的执行和线程池的关闭等过程。通过对线程池源码的解析,我们能够更好地理解线程池的原理和机制,为我们在实际开发中合理使用线程池提供指导。文章内容较长,建议找个安静的环境慢慢细读,由于线程池涉及的内容......
  • 获得详细错误信息-windows核心编程
    windows程序员想要知道系统出错信息,一般使用GetLastError()来获取,该API获取的是错误码,如通过GetLastError返回123我们可以查询VS自带工具errorloop得到出错码123的错误信息:文件名、目录名或卷标语法不正确。这是一件挺麻烦的事。所以如果开发过程中我们能直接得到错误码的错误信息......
  • 全民用上“文心一言”!各类文案创作、编程辅助、问答咨询需求巨大
    9月17日,由中国人工智能学会、江西省科学技术厅、南昌市人民政府共同主办的2023第十二届中国智能产业高峰论坛(CIIS2023)在江西南昌开幕。中国工程院戴琼海院士、赵春江院士,蒋昌俊院士等多位高校科研院所和企业嘉宾分享学术前沿、展示技术创新,共同推动智能产业高质量发展。百度自研大......
  • Java并发Map的面试指南:线程安全数据结构的奥秘
    简介在计算机软件开发的世界里,多线程编程是一个重要且令人兴奋的领域。然而,与其引人入胜的潜力相伴而来的是复杂性和挑战,其中之一就是处理共享数据。当多个线程同时访问和修改共享数据时,很容易出现各种问题,如竞态条件和数据不一致性。本文将探讨如何在Java中有效地应对这些挑战,介......
  • Java并发Map的面试指南:线程安全数据结构的奥秘
    简介在计算机软件开发的世界里,多线程编程是一个重要且令人兴奋的领域。然而,与其引人入胜的潜力相伴而来的是复杂性和挑战,其中之一就是处理共享数据。当多个线程同时访问和修改共享数据时,很容易出现各种问题,如竞态条件和数据不一致性。本文将探讨如何在Java中有效地应对这些挑战,......