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
通常用于在回调函数中确保临界区锁的安全释放。它们的选择取决于你的代码逻辑和使用场景。