首先来看一个例子,从这个例子我们开始讨论这个问题
void main(){ HANDLE hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadCallBack,NULL,0,NULL); for (int i=0;i<100000;i++) { g1++; g2++; } cout<<
在主函数中有一个线程进行for循环累加计算,但是子线程中也在进行计算,这势必会让两个线程的计算会交叉进行,导致最终输出的结果并不是我们想要的。
在这里我有两种方法解决这个问题:
一、使用临界区的
首先解释什么是临界区:在任意时刻只允许一个线程对共享资源进行访问的区域,也就是多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。 如果一个
线程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。为了确保读线程读取到的是经过修改的变量,就必须在向变量写入
数据时禁止其他线程对其的任何访问,直至赋值过程结束后再解除对其他线程的访问限制。
CRITICAL_SECTION cs; //临界区的声明void main(){ InitializeCriticalSection(&cs); //初始化临界区 HANDLE hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadCallBack,NULL,0,NULL); EnterCriticalSection(&cs); //进入临界区 for (int i=0;i<100000;i++) { g1++; g2++; } LeaveCriticalSection(&cs); //执行完出来 WaitForSingleObject(hThread,INFINITE); //等子线程执行完 cout<<
使用临界区的方法之前要声明和初始化临界区。每个线程进入临界区之前,声明一下EnterCriticalSection(&cs)进入临界区,在线程执行完毕之后,LeaveCriticalSection(&cs)退出临界区,接着让其他线程进入临界区。
在最后所有线程执行完毕时,要销毁临界区。
二、设置事件
HANDLE hEvent; //线程同步 事件void main(){ hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); HANDLE hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadCallBack,NULL,0,NULL); for (int i=0;i<100000;i++) { g1++; g2++; } SetEvent(hEvent); //设置事件信号 WaitForSingleObject(hThread,INFINITE); //等子线程执行完 cout<<
首先是创建事件,此时的事件还未受信。当主线程执行完毕时,设置事件受信,子线程中的WaitForSingleObject函数收到信号后,会向下执行。在线程执行完成后,要关闭事件句柄。
另外有几个小问题:
一、函数的调用约定
函数的调用约定就是描述参数是怎么传递和由谁平衡堆栈的,当然还有返回值。
常用的调用约定如下:
_cdecl
1、参数是从右向左传递的,也是放在堆栈中。
2、堆栈平衡是由调用函数来执行的。
_stdcall
Win32 API函数绝大部分都是采用__stdcall调用约定的。WINAPI也是__stacall的一个别名。
1、参数是从右往左传递的,也是放在堆栈中。
二、句柄是什么?
句柄是一个标识符,是拿来标识对象或者项目的。
一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序中为8字节)长的数值,来标识应用程序中的不同对象和同类中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。
应用程序能够通过句柄访问相应的对象的信息,但是句柄不是指针,程序不能利用句柄来直接阅读文件中的信息。
如果句柄不在I/O文件中,它是毫无用处的。
句柄是Windows用来标志应用程序中建立的或是使用的唯一整数,Windows大量使用了句柄来标识对象。