【C语言】Windows下的多线程编程-原子操作
在计算机中,原子操作又称为原语,操作系统保证:原子操作是不可分割的,在执行完毕之前不会被任何其它任务或事件中断。
下面列出一些常用的Interlocked系列函数:
InterlockedIncrement自增
/** \brief 实现数的原子性自减** \param lpAddend 指向要递增的变量的指针* \return 返回自增后的变量值**/
LONG InterlockedIncrement(LPLONG lpAddend);
在C中相当于
int i = 0;
i++;
为了保证原子性,应
InterlockedIncrement((long*)&i);
举个例子:如果一个变量 long value =0;
首先说一下正常情况下的加减操作:value+=1;
1:系统从Value的空间取出值,并动态生成一个空间来存储取出来的值;
2:将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束。
如果此时有两个Thread ,分别记作threadA,threadB。
1:threadA将Value从存储空间取出,为0;
2:threadB将Value从存储空间取出,为0;
3:threadA将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束,Value=1。
4:threadB将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束,Value=1。
最后Value =1 ,而正确应该是2;这就是问题的所在,InterlockedIncrement能够保证在一个线程访问变量时其它线程不能访问。同理InterlockedDecrement。
InterlockedDecrement自减
/** \brief 实现数的原子性自增** \param lpAddend 指向要递减的变量的指针* \return 返回自减后的变量值**/
LONG InterlockedDecrement(LPLONG lpAddend);
在C中相当于
int i = 0;
i--;
为了保证原子性,应
InterlockedDecrement((long*)&i);
InterlockedExchangeAdd加减
/** \brief 实现数的原子性自增** \param Addend 指向要加减的变量的指针* \param Increment 要加减的值* \return 返回变量值**/
LONG InterlockedExchangeAdd(LPLONG Addend, LONG Increment);
示例
InterlockedExchangeAdd((long*)&i, 5);//等价于 i = i + 5;
InterlockedExchange赋值
/** \brief 实现数的原子性赋值** \param Addend 指向要赋值的变量的指针* \param Value 要加减的值* \return 返回变量值**/
LONG InterlockedExchange(LPLONG Addend, LONG Value);
示例
InterlockedExchange((long*)&i, 10);//等价于 i = 5;
示例
模拟用户登录(线程),登录一次则记录一次,最终输出有多少个用户登录的多少次。
#include <stdio.h>
#include <Windows.h>
#include <process.h>#define THREAD_COUNT 10U //用户数void Thread(void*);volatile int count = 0;//记录登录的次数int main()
{HANDLE th[THREAD_COUNT] = { 0 };int num = 30;//模拟次数while (num--){count = 0;//重置登录次数for (size_t i = 0; i < THREAD_COUNT; i++){/* 创建线程 */th[i] = _beginthread(Thread, 0, NULL);}//等待所有线程结束WaitForMultipleObjects(THREAD_COUNT, th, TRUE, INFINITE);printf("共 %d 个用户 最终登录的次数为%d\n", THREAD_COUNT, count);}return 0;
}void Thread(void* arg)
{Sleep(100);count++;
}
输出结果
输出结果把变量自增操作封装为原子性操作
#include <stdio.h>
#include <Windows.h>
#include <process.h>#define THREAD_COUNT 10U //用户数void Thread(void*);volatile int count = 0;//记录登录的次数int main()
{HANDLE th[THREAD_COUNT] = { 0 };int num = 30;//模拟次数while (num--){count = 0;//重置登录次数for (size_t i = 0; i < THREAD_COUNT; i++){/* 创建线程 */th[i] = _beginthread(Thread, 0, NULL);}//等待所有线程结束WaitForMultipleObjects(THREAD_COUNT, th, TRUE, INFINITE);printf("共 %d 个用户 最终登录的次数为%d\n", THREAD_COUNT, count);}return 0;
}void Thread(void* arg)
{Sleep(100);//count++;InterlockedIncrement((long*)&count);
}
输出结果