事件是内核对象。事件的常用函数:
CreateEvent
函数功能:创建事件
函数原型:
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR pName
);
第一个参数表示安全控制,一般直接传入NULL。
第二个参数确定事件是手动置位还是自动置位,传入TRUE表示手动置位,传入FALSE表示自动置位。如果为自动置位,则对该事件调用WaitForSingleObject()后会自动调用ResetEvent()使事件变成未触发状态。
第三个参数表示事件的初始状态,传入TRUE表示已触发。
第四个参数表示事件的名称,传入NULL表示匿名事件。
OpenEvent
函数功能:根据名称获得一个事件句柄。
函数原型:
HANDLE OpenEvent(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTRl pName //名称
);
函数说明:
第一个参数表示访问权限,对事件一般传入EVENT_ALL_ACCESS。
第二个参数表示事件句柄继承性,一般传入TRUE即可。
第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个事件。
SetEvent
函数功能:触发事件
函数原型:BOOLSetEvent(HANDLEhEvent);
函数说明:每次触发后,必有一个或多个处于等待状态下的线程变成可调度状态。
ResetEvent
函数功能:将事件设为末触发
函数原型:BOOLResetEvent(HANDLEhEvent);
CloseHandle
清理与销毁
由于事件是内核对象,因此使用CloseHandle()就可以完成清理与销毁了。
多线程中的调试,由于调试是对于当前线程的,所以就导致一个出不来的情况,即线程中断后,你点继续,然后操作系统有将线程运行,而导致调试时线程永远是强制中断,强制运行,而不会进入其他线程。。。
线程同步主要研究的问题是,由于创建多个线程,而线程的运行的顺序是不定的,导致出现一些不合理的情况,所以提出同步的概念,使线程按照一定顺序运行。
总结一下同步互斥,我们有可以发现,同步互斥都不是针对某块资源实现的,而是在任何情况下访问任何资源的可以同步互斥,而真正的同步互斥是在对共享资源进行操作的代码处加上同步互斥的约定,使对这些共享资源的访问是同步互斥的,而不是在一些线程自己的资源上也进行同步互斥的费操作。
如下是事件的代码实现。
HANDLE mutex = INVALID_HANDLE_VALUE; HANDLE event; typedef struct param{ int a; bool b; double c; string d; }; unsigned int __stdcall threadfun1(LPVOID p){ Sleep(100); //调用等待互斥量未触发,将其有未触发改为触发 WaitForSingleObject(mutex,INFINITE); param* funparam = (param*) p; cout<<"fun1 running:"<<funparam->d<<endl; funparam->c *= funparam->a; cout<<"ans:"<<funparam->c<<endl; //释放互斥量,改为未触发 ReleaseMutex(mutex); return 0; } DWORD __stdcall threadfun2(LPVOID p){ Sleep(100); //调用等待互斥量未触发,将其有未触发改为触发 WaitForSingleObject(mutex,INFINITE); SetEvent(event); param* funparam = (param*) p; cout<<"fun2 running:"<<funparam->d<<endl; funparam->c /= (1+funparam->a); cout<<funparam->a<<"\nans:"<<funparam->c<<endl; //释放互斥量,改为未触发 ReleaseMutex(mutex); return 0; } int main(){ param p={1,true,9.995,"hello world"}; //A表示字符串使用的是ASCII编码集,默认为UNICODE,即W。 //第二个参数为false,表示互斥量未触发,当为true表示已触发。 mutex = CreateMutexA(NULL,false,"MUTEX"); //对于进程间的互斥,在一个进程创建互斥量,然后在另外一个进程打开互斥量 mutex = OpenMutexA(MUTEX_ALL_ACCESS,true,"MUTEX"); HANDLE newthread1 = (HANDLE)_beginthreadex(NULL,0,threadfun1,&p,0,NULL); event = CreateEvent(NULL,false,false,NULL); const int THREADNUM = 10; HANDLE newthread2[THREADNUM]; int &i =p.a; while(i<THREADNUM){ newthread2[i]= CreateThread(NULL,0,threadfun2,&p,0,NULL); WaitForSingleObject(event,INFINITE); //WaitForSingleObject(newthread2[i],INFINITE); i++; } WaitForMultipleObjects(THREADNUM,newthread2,true,INFINITE); //WaitForSingleObject(newthread2,INFINITE); WaitForSingleObject(newthread1,INFINITE); CloseHandle(mutex); CloseHandle(event); system("PAUSE"); }
对于这里,使用自动档,使用waitforsingleobject来使事件未触发,而用手动档,调用ReleaseEvent()释放。
仔细分析一下,发现其实同步根本没有你们复杂,就比如这个地方,要实现线程的先后顺序,只要等待一个线程完成即可,直接使用WaitForSingleObject(newthread2[i],INFINITE)就可以等待 第i个线程完成后再运行下一步,即实现同步,也可以改为释放互斥量或退出临界区的操作。
实际上线程同步就是互斥,而只要对互斥进行一点简单的变形就可以得到同步,如等待同步。所以同步互斥都是可以用一种方法来实现的,但为什么会有同步互斥,以及这些大同小异的方法呢?