我们能不能来一个线程报数功能,即第一个子线程输出1,第二个子线程输出2,第三个子线程输出3,……。要实现这个功能似乎非常简单——每个子线程对一个全局变量进行递增并输出就可以了。
代码如下:
//子线程报数 #include <stdio.h> #include <process.h> #include <windows.h> int g_nCount; //子线程函数 unsigned int __stdcall ThreadFun(PVOID pM) { g_nCount++; printf("线程ID号为%4d的子线程报数%d\n", GetCurrentThreadId(), g_nCount); return 0; } //主函数,所谓主函数其实就是主线程执行的函数。 int main() { printf(" 子线程报数 \n"); const int THREAD_NUM = 10; HANDLE handle[THREAD_NUM]; g_nCount = 0; for (int i = 0; i < THREAD_NUM; i++) handle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL); WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); return 0; }
下面为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是否运行出错。这也非常类似于统计一个网站每天有多少用户登录,每个用户登录用一个线程模拟,线程运行时会将一个表示计数的变量递增。程序在最后输出计数的值表示有今天多少个用户登录,如果这个值不等于我们启动的线程个数,那显然说明这个程序是有问题的。
代码如下:
#include <stdio.h> #include <process.h> #include <windows.h> volatile long g_nLoginCount; //登录次数 unsigned int __stdcall Fun(void *pPM); //线程函数 const int THREAD_NUM = 10; //启动线程数 unsigned int __stdcall ThreadFun(void *pPM) { Sleep(100); //some work should to do g_nLoginCount++; Sleep(50); return 0; } int main() { g_nLoginCount = 0; HANDLE handle[THREAD_NUM]; for (int i = 0; i < THREAD_NUM; i++) handle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL); WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); printf("有%d个用户登录后记录结果是%d\n", THREAD_NUM, g_nLoginCount); return 0; }
代码如下:
#include <stdio.h> #include <windows.h> volatile long g_nLoginCount; //登录次数 unsigned int __stdcall Fun(void *pPM); //线程函数 const DWORD THREAD_NUM = 50;//启动线程数 DWORD WINAPI ThreadFun(void *pPM) { Sleep(100); //some work should to do g_nLoginCount++; Sleep(50); return 0; } int main() { printf(" 原子操作 Interlocked系列函数的使用\n"); printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n"); //重复20次以便观察多线程访问同一资源时导致的冲突 int num= 20; while (num--) { g_nLoginCount = 0; int i; HANDLE handle[THREAD_NUM]; for (i = 0; i < THREAD_NUM; i++) handle[i] = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL); WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); printf("有%d个用户登录后记录结果是%d\n", THREAD_NUM, g_nLoginCount); } return 0; }
讲解下这三条汇编意思:
第一条汇编将g_nLoginCount的值从内存中读取到寄存器eax中。
第二条汇编将寄存器eax中的值与1相加,计算结果仍存入寄存器eax中。
第三条汇编将寄存器eax中的值写回内存中。
出现错误可能的原因是,A,B线程都读取了同一个值,然后相加后,写入到 memory 中,这样实际上 A 和 B 是只对 global value 加了一次。这样执行下来,结果是不可预知的——可能会出现50,可能小于50。
因此在多线程环境中对一个变量进行读写时,我们需要有一种方法能够保证对一个值的递增操作是原子操作——即不可打断性,一个线程在执行原子操作时,其它线程必须等待它完成之后才能开始执行该原子操作。这种涉及到硬件的操作会不会很复杂了,幸运的是,Windows系统为我们提供了一些以Interlocked开头的函数来完成这一任务。下一篇我们会分析一下Interlocked函数的应用。
文中的代码均来自morewindows大大的博客。
原文地址:http://blog.csdn.net/djd1234567/article/details/46011285