??xml version="1.0" encoding="utf-8" standalone="yes"?>囯产精品久久久久久久久蜜桃,欧美精品丝袜久久久中文字幕,一本色道久久88—综合亚洲精品 http://www.shnenglu.com/tdweng/category/15115.htmlzh-cn Fri, 17 Dec 2010 05:28:29 GMT Fri, 17 Dec 2010 05:28:29 GMT 60 InterLockedIncrement and InterLockedDecrement http://www.shnenglu.com/tdweng/articles/136461.html心羽 心羽 Wed, 15 Dec 2010 01:55:00 GMT http://www.shnenglu.com/tdweng/articles/136461.html http://www.shnenglu.com/tdweng/comments/136461.html http://www.shnenglu.com/tdweng/articles/136461.html#Feedback 0 http://www.shnenglu.com/tdweng/comments/commentRss/136461.html http://www.shnenglu.com/tdweng/services/trackbacks/136461.html 实现数的原子性加减。什么是原子性的加减呢?
InterLockedIncrement 举个例子Q如果一个变?Long value =0;
首先说一下正常情况下的加减操作:(x)value+=1Q?/p>
1Q系l从Value的空间取出|q动态生成一个空间来存储取出来的|
2Q将取出来的值和1作加法,q且和攑֛Value的空间覆盖掉原倹{加法结束?/p>
如果此时有两个Thread Q分别记作threadAQthreadB?/p>
1QthreadAValue从存储空间取出,?Q?/p>
2QthreadBValue从存储空间取出,?Q?/p>
3QthreadA取出来的值和1作加法,q且和攑֛Value的空间覆盖掉原倹{加法结束,Value=1?/p>
4QthreadB取出来的值和1作加法,q且和攑֛Value的空间覆盖掉原倹{加法结束,Value=1?/p>
最后Value =1 Q而正应该是2Q这是问题的所在,InterLockedIncrement 能够保证在一个线E访问变量时其它U程不能讉K?br>例:(x)如果 static long addref=0; ?InterlockedIncrement(&addref); ?addref=1 InterLockedDecrement
LONG InterlockedDecrement( LPLONG lpAddend // variable address ); 属于互锁函数Q用在同一q程内,需要对׃n的一个变量,做减法的时候, 防止其他U程讉Kq个变量Q是实现U程同步的一U办法(互锁函数Q? 首先要理解多U程同步Q共享资源(同时讉K全局变量的问题)Q否则就难以理解?nbsp; result = InterlockedDecrementQ?amp;SomeIntQ?nbsp; 如果不考虑多线E其实就?nbsp; result = SomeInt - 1; 但是考虑到多U程问题复杂了一些。就是说如果惌得到我预期的l果q不Ҏ(gu)?nbsp; result = SomeInt - 1Q?nbsp; 举例? SomeInt如果==1; 预期的结果result当然==0; 但是,如果SomeInt是一个全E共享的全局变量情况׃一样了?nbsp; C语言?result = SomeInt - 1Q? 在实际的执行q程中,有好几条指o(h)Q在指o(h)执行q程中,其它U程可能改变SomeInt|使真正的l果与你预期的不一致?nbsp; 所以InterlockedDecrement(&SomeInt)的执行过E是q样?nbsp; { __止其他U程讉K (&SomeInt) q个地址 SomeInt --; move EAX, someInt; // 讑֮q回?C++函数的返回?nbsp; 都放在EAX? __开攑օ他线E访?nbsp; (&SomeInt) q个地址 } 但是实际上只需要几条指令加前缀可以完成,以上说明是放大的?nbsp; 你也怼(x)_(d)q有必要? 一般来_(d)发生错误的概率不大,但是防范L必要?
例:(x)如果 static long addref=0; ?InterlockedDecrement(&addref); ?addref=-1
]]>【{】一个简单的U程池(c++版) http://www.shnenglu.com/tdweng/articles/129161.html心羽 心羽 Sat, 09 Oct 2010 01:42:00 GMT http://www.shnenglu.com/tdweng/articles/129161.html http://www.shnenglu.com/tdweng/comments/129161.html http://www.shnenglu.com/tdweng/articles/129161.html#Feedback 0 http://www.shnenglu.com/tdweng/comments/commentRss/129161.html http://www.shnenglu.com/tdweng/services/trackbacks/129161.html #ifndef _ThreadPool_H_ #define _ThreadPool_H_ #pragma warning(disable: 4530) #pragma warning(disable: 4786) #include <cassert> #include <vector> #include <queue> #include <windows.h> class ThreadJob //工作基类 { public: //供线E池调用的虚函数 virtual void DoJob(void *pPara) = 0; }; class ThreadPool { public: //dwNum U程池规?br> ThreadPool(DWORD dwNum = 4) : _lThreadNum(0), _lRunningNum(0) { InitializeCriticalSection(&_csThreadVector); InitializeCriticalSection(&_csWorkQueue); _EventComplete = CreateEvent(0, false, false, NULL); _EventEnd = CreateEvent(0, true, false, NULL); _SemaphoreCall = CreateSemaphore(0, 0, 0x7FFFFFFF, NULL); _SemaphoreDel = CreateSemaphore(0, 0, 0x7FFFFFFF, NULL); assert(_SemaphoreCall != INVALID_HANDLE_VALUE); assert(_EventComplete != INVALID_HANDLE_VALUE); assert(_EventEnd != INVALID_HANDLE_VALUE); assert(_SemaphoreDel != INVALID_HANDLE_VALUE); AdjustSize(dwNum <= 0 ? 4 : dwNum); } ~ThreadPool() { DeleteCriticalSection(&_csWorkQueue); CloseHandle(_EventEnd); CloseHandle(_EventComplete); CloseHandle(_SemaphoreCall); CloseHandle(_SemaphoreDel); vector<ThreadItem*>::iterator iter; for(iter = _ThreadVector.begin(); iter != _ThreadVector.end(); iter++) { if(*iter) delete *iter; } DeleteCriticalSection(&_csThreadVector); } //调整U程池规?br> int AdjustSize(int iNum) { if(iNum > 0) { ThreadItem *pNew; EnterCriticalSection(&_csThreadVector); for(int _i=0; _i<iNum; _i++) { _ThreadVector.push_back(pNew = new ThreadItem(this)); assert(pNew); pNew->_Handle = CreateThread(NULL, 0, DefaultJobProc, pNew, 0, NULL); assert(pNew->_Handle); } LeaveCriticalSection(&_csThreadVector); } else { iNum *= -1; ReleaseSemaphore(_SemaphoreDel, iNum > _lThreadNum ? _lThreadNum : iNum, NULL); } return (int)_lThreadNum; } //调用U程?br> void Call(void (*pFunc)(void *), void *pPara = NULL) { assert(pFunc); EnterCriticalSection(&_csWorkQueue); _JobQueue.push(new JobItem(pFunc, pPara)); LeaveCriticalSection(&_csWorkQueue); ReleaseSemaphore(_SemaphoreCall, 1, NULL); } //调用U程?br> inline void Call(ThreadJob * p, void *pPara = NULL) { Call(CallProc, new CallProcPara(p, pPara)); } //l束U程? q同步等?br> bool EndAndWait(DWORD dwWaitTime = INFINITE) { SetEvent(_EventEnd); return WaitForSingleObject(_EventComplete, dwWaitTime) == WAIT_OBJECT_0; } //l束U程?br> inline void End() { SetEvent(_EventEnd); } inline DWORD Size() { return (DWORD)_lThreadNum; } inline DWORD GetRunningSize() { return (DWORD)_lRunningNum; } bool IsRunning() { return _lRunningNum > 0; } protected: //工作U程 static DWORD WINAPI DefaultJobProc(LPVOID lpParameter = NULL) { ThreadItem *pThread = static_cast<ThreadItem*>(lpParameter); assert(pThread); ThreadPool *pThreadPoolObj = pThread->_pThis; assert(pThreadPoolObj); InterlockedIncrement(&pThreadPoolObj->_lThreadNum); HANDLE hWaitHandle[3]; hWaitHandle[0] = pThreadPoolObj->_SemaphoreCall; hWaitHandle[1] = pThreadPoolObj->_SemaphoreDel; hWaitHandle[2] = pThreadPoolObj->_EventEnd; JobItem *pJob; bool fHasJob; for(;;) { DWORD wr = WaitForMultipleObjects(3, hWaitHandle, false, INFINITE); //响应删除U程信号 if(wr == WAIT_OBJECT_0 + 1) break; //从队列里取得用户作业 EnterCriticalSection(&pThreadPoolObj->_csWorkQueue); if(fHasJob = !pThreadPoolObj->_JobQueue.empty()) { pJob = pThreadPoolObj->_JobQueue.front(); pThreadPoolObj->_JobQueue.pop(); assert(pJob); } LeaveCriticalSection(&pThreadPoolObj->_csWorkQueue); //受到l束U程信号定是否l束U程(l束U程信号&& 是否q有工作) if(wr == WAIT_OBJECT_0 + 2 && !fHasJob) break; if(fHasJob && pJob) { InterlockedIncrement(&pThreadPoolObj->_lRunningNum); pThread->_dwLastBeginTime = GetTickCount(); pThread->_dwCount++; pThread->_fIsRunning = true; pJob->_pFunc(pJob->_pPara); //q行用户作业 delete pJob; pThread->_fIsRunning = false; InterlockedDecrement(&pThreadPoolObj->_lRunningNum); } } //删除自nl构 EnterCriticalSection(&pThreadPoolObj->_csThreadVector); pThreadPoolObj->_ThreadVector.erase(find(pThreadPoolObj->_ThreadVector.begin(), pThreadPoolObj->_ThreadVector.end(), pThread)); LeaveCriticalSection(&pThreadPoolObj->_csThreadVector); delete pThread; InterlockedDecrement(&pThreadPoolObj->_lThreadNum); if(!pThreadPoolObj->_lThreadNum) //所有线E结?br> SetEvent(pThreadPoolObj->_EventComplete); return 0; } //调用用户对象虚函?br> static void CallProc(void *pPara) { CallProcPara *cp = static_cast<CallProcPara *>(pPara); assert(cp); if(cp) { cp->_pObj->DoJob(cp->_pPara); delete cp; } } //用户对象l构 struct CallProcPara { ThreadJob* _pObj;//用户对象 void *_pPara;//用户参数 CallProcPara(ThreadJob* p, void *pPara) : _pObj(p), _pPara(pPara) { }; }; //用户函数l构 struct JobItem { void (*_pFunc)(void *);//函数 void *_pPara; //参数 JobItem(void (*pFunc)(void *) = NULL, void *pPara = NULL) : _pFunc(pFunc), _pPara(pPara) { }; }; //U程池中的线E结?br> struct ThreadItem { HANDLE _Handle; //U程句柄 ThreadPool *_pThis; //U程池的指针 DWORD _dwLastBeginTime; //最后一ơ运行开始时?br> DWORD _dwCount; //q行ơ数 bool _fIsRunning; ThreadItem(ThreadPool *pthis) : _pThis(pthis), _Handle(NULL), _dwLastBeginTime(0), _dwCount(0), _fIsRunning(false) { }; ~ThreadItem() { if(_Handle) { CloseHandle(_Handle); _Handle = NULL; } } }; std::queue<JobItem *> _JobQueue; //工作队列 std::vector<ThreadItem *> _ThreadVector; //U程数据 CRITICAL_SECTION _csThreadVector, _csWorkQueue; //工作队列临界, U程数据临界 HANDLE _EventEnd, _EventComplete, _SemaphoreCall, _SemaphoreDel;//l束通知, 完成事g, 工作信号Q删除线E信?br> long _lThreadNum, _lRunningNum; //U程? q行的线E数 }; #endif //_ThreadPool_H_
使用说明1Q?/p>
调用Ҏ(gu)
void threadfunc(void *p)
{
YourClass* yourObject = (YourClass*) p;
//
}
ThreadPool tp;
for(i=0; i<100; i++)
tp.Call(threadfunc);
ThreadPool tp(20);//20为初始线E池规模
tp.Call(threadfunc, lpPara);
使用时注意几点:(x)
1. ThreadJob 没什么用Q直接写U程函数吧?nbsp;
2. U程函数QthreadfuncQ的入口参数void* 可以转成自定义的cd对象Q这个对象可以记录下U程q行中的数据Qƈ讄U程当前状态,以此与线E进行交互?/p>
3. U程池有一个EndAndWait函数Q用于让U程池中所有计正常结束。有时线E池中的一个线E可能要q行很长旉Q怎么办?可以通过U程函数threadfunc的入口参数对象来处理Q比如:(x)
class YourClass { int cmd; // cmd = 1是上U程停止计算Q正帔R出?br>}; threadfunc(void* p) { YourClass* yourObject = (YourClass*)p; while (true) { // do some calculation if (yourClass->cmd == 1) break; } }
在主U程中设|yourClass->cmd = 1Q该U程׃(x)自然l束?/p>
使用说明2Q?/p>
Code
void threadfunc(void *p)
{
//
}
ThreadPool tp;
for(i=0; i<100; i++)
tp.Call(threadfunc);
ThreadPool tp(20);//20为初始线E池规模
tp.Call(threadfunc, lpPara);
tp.AdjustSize(50);//增加50
tp.AdjustSize(-30);//减少30
class MyThreadJob : public ThreadJob //U程对象从ThreadJob扩展
{
public:
virtual void DoJob(void *p)//自定义的虚函?br> {
//
.
}
};
MyThreadJob mt[10];
ThreadPool tp;
for(i=0; i<100 i++)
tp.Call(mt + i);//tp.Call(mt + i, para);
]]> Visual C++U程同步技?? http://www.shnenglu.com/tdweng/articles/124796.html心羽 心羽 Thu, 26 Aug 2010 03:38:00 GMT http://www.shnenglu.com/tdweng/articles/124796.html http://www.shnenglu.com/tdweng/comments/124796.html http://www.shnenglu.com/tdweng/articles/124796.html#Feedback 0 http://www.shnenglu.com/tdweng/comments/commentRss/124796.html http://www.shnenglu.com/tdweng/services/trackbacks/124796.html U程同步的方式有Q?br> 临界?br> 理事g内核对象 信号量内核对?br> 互斥内核对象 分别介绍如下Q?br> 使线E同?br> 在程序中使用多线E时Q一般很有多个U程能在其生命期内进行完全独立的操作。更多的情况是一些线E进行某些处理操作,而其他的U程必须对其处理l果q行了解。正常情况下对这U处理结果的了解应当在其处理d完成后进行?br> 如果不采取适当的措施,其他U程往往?x)在U程处理dl束前就去访问处理结果,q就很有可能得到有关处理l果的错误了解。例如,多个U程同时讉K同一个全局变量Q如果都是读取操作,则不?x)出现问题。如果一个线E负责改变此变量的|而其他线E负责同时读取变量内容,则不能保证读取到的数据是l过写线E修改后的?br> Z保ȝE读取到的是l过修改的变量,必d向变量写入数据时止其他U程对其的Q何访问,直至赋DE结束后再解除对其他U程的访问限制。象q种保证U程能了解其他线EQ务处理结束后的处理结果而采取的保护措施即ؓ(f)U程同步?br> U程同步是一个非常大的话题,包括Ҏ(gu)面面的内宏V从大的斚wԌU程的同步可分用h式的U程同步和内核对象的U程同步两大cR用h式中U程的同步方法主要有原子讉K和(f)界区{方法。其特点是同步速度特别快,适合于对U程q行速度有严D求的场合?br> 内核对象的线E同步则主要׃件、等待定时器、信号量以及(qing)信号灯等内核对象构成。由于这U同步机制用了内核对象Q用时必须线E从用户模式切换到内核模式,而这U{换一般要耗费q千个CPU周期Q因此同步速度较慢Q但在适用性上却要q优于用h式的U程同步方式?br>临界?/strong> 临界区(Critical SectionQ是一D늋占对某些׃n资源讉K的代码,在Q意时d允许一个线E对׃n资源q行讉K。如果有多个U程试图同时讉K临界区,那么在有一个线E进入后其他所有试图访问此临界区的U程被挂vQƈ一直持l到q入临界区的U程d。(f)界区在被释放后,其他U程可以l箋抢占Qƈ以此辑ֈ用原子方式操作共享资源的目的?br> 临界区在使用时以CRITICAL_SECTIONl构对象保护׃n资源Qƈ分别用EnterCriticalSectionQ)和LeaveCriticalSectionQ)函数L识和释放一个(f)界区。所用到的CRITICAL_SECTIONl构对象必须l过InitializeCriticalSectionQ)的初始化后才能用,而且必须保所有线E中的Q何试图访问此׃n资源的代码都处在此(f)界区的保护之下。否则(f)界区不?x)v到应有的作用Q共享资源依然有被破坏的可能?br> ? 使用临界Z持线E同?br> 下面通过一D代码展CZ临界区在保护多线E访问的׃n资源中的作用。通过两个U程来分别对全局变量g_cArray[10]q行写入操作Q用临界区结构对象g_cs来保持线E的同步Qƈ在开启线E前对其q行初始化。ؓ(f)了实验效果更加明显Q体现出临界区的作用Q在U程函数对共享资源g_cArray[10]的写入时Q以SleepQ)函数延迟1毫秒Q其他U程同其抢占CPU的可能性增大。如果不使用临界区对其进行保护,则共享资源数据将被破坏(参见?QaQ所C结果)Q而用(f)界区对线E保持同步后则可以得到正的l果Q参见图1QbQ所C结果)。代码实现清单附下:(x)
// 临界区结构对?br>CRITICAL_SECTION g_cs; // ׃n资源 char g_cArray[10]; UINT ThreadProc10(LPVOID pParam) { // q入临界?br> EnterCriticalSection(&g_cs); // 对共享资源进行写入操?br> for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // d临界?br> LeaveCriticalSection(&g_cs); return 0; } UINT ThreadProc11(LPVOID pParam) { // q入临界?br> EnterCriticalSection(&g_cs); // 对共享资源进行写入操?br> for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } // d临界?br> LeaveCriticalSection(&g_cs); return 0; } …… void CSample08View::OnCriticalSection() { // 初始化(f)界区 InitializeCriticalSection(&g_cs); // 启动U程 AfxBeginThread(ThreadProc10, NULL); AfxBeginThread(ThreadProc11, NULL); // {待计算完毕 Sleep(300); // 报告计算l果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); }
在用(f)界区Ӟ一般不允许其运行时间过长,只要q入临界区的U程q没有离开Q其他所有试图进入此临界区的U程都会(x)被挂赯进入到{待状态,q会(x)在一定程度上影响。程序的q行性能。尤光要注意的是不要将{待用户输入或是其他一些外界干预的操作包含C(f)界区。如果进入了临界区却一直没有释放,同样也会(x)引v其他U程的长旉{待。换句话_(d)在执行了EnterCriticalSectionQ)语句q入临界区后无论发生什么,必须保与之匚w的LeaveCriticalSectionQ)都能够被执行到。可以通过dl构化异常处理代码来保LeaveCriticalSectionQ)语句的执行。虽然(f)界区同步速度很快Q但却只能用来同步本q程内的U程Q而不可用来同步多个进E中的线E?br> MFCZ(f)界区提供有一个CCriticalSectionc,使用该类q行U程同步处理是非常简单的Q只需在线E函C用CCriticalSectioncL员函数LockQ)和UnLockQ)标定保护代码片段卛_。对于上qC码,可通过CCriticalSectioncd其改写如下:(x)
// MFC临界区类对象 CCriticalSection g_clsCriticalSection; // ׃n资源 char g_cArray[10]; UINT ThreadProc20(LPVOID pParam) { // q入临界?br> g_clsCriticalSection.Lock(); // 对共享资源进行写入操?br> for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // d临界?br> g_clsCriticalSection.Unlock(); return 0; } UINT ThreadProc21(LPVOID pParam) { // q入临界?br> g_clsCriticalSection.Lock(); // 对共享资源进行写入操?br> for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } // d临界?br> g_clsCriticalSection.Unlock(); return 0; } …… void CSample08View::OnCriticalSectionMfc() { // 启动U程 AfxBeginThread(ThreadProc20, NULL); AfxBeginThread(ThreadProc21, NULL); // {待计算完毕 Sleep(300); // 报告计算l果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); }
理事g内核对象 在前面讲q线E通信时曾使用q事件内核对象来q行U程间的通信Q除此之外,事g内核对象也可以通过通知操作的方式来保持U程的同步。对于前面那D用(f)界区保持U程同步的代码可用事件对象的U程同步Ҏ(gu)改写如下Q?br>
// 事g句柄 HANDLE hEvent = NULL; // ׃n资源 char g_cArray[10]; …… UINT ThreadProc12(LPVOID pParam) { // {待事g|位 WaitForSingleObject(hEvent, INFINITE);//有信h才能q入Q若创徏事g句柄为手动复原,下一句应设ؓ(f) // 对共享资源进行写入操?nbsp; //ResetEvent(hEvent),设ؓ(f)无信P若ؓ(f)自动复原Q自动设为无信号?br> for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // 处理完成后即事件对象置?br> SetEvent(hEvent);//设ؓ(f)有信?br> return 0; } UINT ThreadProc13(LPVOID pParam) { // {待事g|位 WaitForSingleObject(hEvent, INFINITE);//有信h才能q入Q若创徏事g句柄为手动复原,下一句应设ؓ(f) // 对共享资源进行写入操?nbsp; //ResetEvent(hEvent),设ؓ(f)无信P若ؓ(f)自动复原Q自动设为无信号?br> for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } // 处理完成后即事件对象置?br> SetEvent(hEvent);//设ؓ(f)有信?br> return 0; } …… void CSample08View::OnEvent() { // 创徏事g hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);//创徏时设动复位,无信?br> // 事g|位 SetEvent(hEvent);//设ؓ(f)有信?br> // 启动U程 AfxBeginThread(ThreadProc12, NULL); AfxBeginThread(ThreadProc13, NULL); // {待计算完毕 Sleep(300); // 报告计算l果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); }
在创建线E前Q首先创Z个可以自动复位的事g内核对象hEventQ而线E函数则通过WaitForSingleObjectQ){待函数无限{待hEvent的置位,只有在事件置位时WaitForSingleObjectQ)才会(x)q回Q被保护的代码将得以执行。对于以自动复位方式创徏的事件对象,在其|位后一被WaitForSingleObjectQ){待到就?x)立卛_位,也就是说在执行ThreadProc12Q)中的受保护代码时Q事件对象已l是复位状态的Q这时即使有ThreadProc13Q)对CPU的抢占,也会(x)׃WaitForSingleObjectQ)没有hEvent的置位而不能l执行,也就没有可能破坏受保护的׃n资源。在ThreadProc12Q)中的处理完成后可以通过SetEventQ)对hEvent的置位而允许ThreadProc13Q)对共享资源g_cArray的处理。这里SetEventQ)所L(fng)作用可以看作是对某项特定d完成的通知?br> 使用临界区只能同步同一q程中的U程Q而用事件内核对象则可以对进E外的线E进行同步,其前提是得到Ҏ(gu)事g对象的访问权。可以通过OpenEventQ)函数获取得到Q其函数原型为:(x)
HANDLE OpenEvent( DWORD dwDesiredAccess, // 讉K标志 BOOL bInheritHandle, // l承标志 LPCTSTR lpName // 指向事g对象名的指针 );
如果事g对象已创建(在创Z件时需要指定事件名Q,函数返回指定事件的句柄。对于那些在创徏事g时没有指定事件名的事件内核对象,可以通过使用内核对象的承性或是调用DuplicateHandleQ)函数来调用CreateEventQ)以获得对指定事g对象的访问权。在获取到访问权后所q行的同步操作与在同一个进E中所q行的线E同步操作是一L(fng)?br> 如果需要在一个线E中{待多个事gQ则用WaitForMultipleObjectsQ)来等待。WaitForMultipleObjectsQ)与WaitForSingleObjectQ)cMQ同时监视位于句柄数l中的所有句柄。这些被监视对象的句柄n有^{的优先权,M一个句柄都不可能比其他句柄h更高的优先权。WaitForMultipleObjectsQ)的函数原型ؓ(f)Q?br>
DWORD WaitForMultipleObjects( DWORD nCount, // {待句柄?br> CONST HANDLE *lpHandles, // 句柄数组首地址 BOOL fWaitAll, // {待标志 DWORD dwMilliseconds // {待旉间隔 );
参数nCount指定了要{待的内核对象的数目Q存放这些内核对象的数组由lpHandles来指向。fWaitAllҎ(gu)定的qnCount个内核对象的两种{待方式q行了指定,为TRUE时当所有对象都被通知时函数才?x)返回,为FALSE则只要其中Q何一个得到通知可以返回。dwMilliseconds在这里的作用与在WaitForSingleObjectQ)中的作用是完全一致的。如果等待超Ӟ函数返回WAIT_TIMEOUT。如果返回WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1中的某个|则说明所有指定对象的状态均为已通知状态(当fWaitAll为TRUEӞ或是用以减去WAIT_OBJECT_0而得到发生通知的对象的索引Q当fWaitAll为FALSEӞ。如果返回值在WAIT_ABANDONED_0与WAIT_ABANDONED_0+nCount-1之间Q则表示所有指定对象的状态均为已通知Q且其中臛_有一个对象是被丢弃的互斥对象Q当fWaitAll为TRUEӞQ或是用以减去WAIT_OBJECT_0表示一个等待正常结束的互斥对象的烦引(当fWaitAll为FALSEӞ?下面l出的代码主要展CZ对WaitForMultipleObjectsQ)函数的用。通过对两个事件内核对象的{待来控制线EQ务的执行与中途退出:(x)
// 存放事g句柄的数l?br>HANDLE hEvents[2]; UINT ThreadProc14(LPVOID pParam) { // {待开启事?br> DWORD dwRet1 = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE); // 如果开启事件到辑ֈU程开始执行Q?br> if (dwRet1 == WAIT_OBJECT_0) { AfxMessageBox("U程开始工?"); while (true) { for (int i = 0; i < 10000; i++); // 在Q务处理过E中{待l束事g DWORD dwRet2 = WaitForMultipleObjects(2, hEvents, FALSE, 0); // 如果l束事g|位则立即终止Q务的执行 if (dwRet2 == WAIT_OBJECT_0 + 1) break; } } AfxMessageBox("U程退?"); return 0; } …… void CSample08View::OnStartEvent() { // 创徏U程 for (int i = 0; i < 2; i++) hEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL); // 开启线E?br> AfxBeginThread(ThreadProc14, NULL); // 讄事g0(开启事? SetEvent(hEvents[0]); } void CSample08View::OnEndevent() { // 讄事g1(l束事g) SetEvent(hEvents[1]); }
MFCZ件相兛_理也提供了一个CEventc,共包含有除构造函数外?个成员函数PulseEventQ)、ResetEventQ)、SetEventQ)和UnLockQ)。在功能上分别相当与Win32 API的PulseEventQ)、ResetEventQ)、SetEventQ)和CloseHandleQ){函数。而构造函数则履行了原CreateEventQ)函数创徏事g对象的职责,其函数原型ؓ(f)Q?br>
CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );
按照此缺省设|将创徏一个自动复位、初始状态ؓ(f)复位状态的没有名字的事件对象。封装后的CEventcM用v来更加方便,?卛_CZCEventcdA、B两线E的同步q程Q?br> ? CEventcdU程的同步过E示?br> BU程在执行到CEventcL员函数LockQ)时将?x)发生阻塞,而AU程此时则可以在没有BU程q扰的情况下对共享资源进行处理,q在处理完成后通过成员函数SetEventQ)向B(ti)发出事gQ其被释放Q得以对A先前已处理完毕的׃n资源q行操作。可见,使用CEventcdU程的同步方法与通过API函数q行U程同步的处理方法是基本一致的。前面的API处理代码可用CEventcd其改写ؓ(f)Q?br>
// MFC事gcd?br>CEvent g_clsEvent; UINT ThreadProc22(LPVOID pParam) { // 对共享资源进行写入操?br> for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // 事g|位 g_clsEvent.SetEvent(); return 0; } UINT ThreadProc23(LPVOID pParam) { // {待事g g_clsEvent.Lock(); // 对共享资源进行写入操?br> for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } return 0; } …… void CSample08View::OnEventMfc() { // 启动U程 AfxBeginThread(ThreadProc22, NULL); AfxBeginThread(ThreadProc23, NULL); // {待计算完毕 Sleep(300); // 报告计算l果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); }
信号量内核对?br> 信号量(SemaphoreQ内核对象对U程的同步方式与前面几种Ҏ(gu)不同Q它允许多个U程在同一时刻讉K同一资源Q但是需要限制在同一时刻讉K此资源的最大线E数目。在用CreateSemaphoreQ)创徏信号量时卌同时指出允许的最大资源计数和当前可用资源计数。一般是当前可用资源计数设|ؓ(f)最大资源计敎ͼ每增加一个线E对׃n资源的访问,当前可用资源计数׃(x)?Q只要当前可用资源计数是大于0的,可以发Z号量信号。但是当前可用计数减到0时则说明当前占用资源的线E数已经辑ֈ了所允许的最大数目,不能在允许其他线E的q入Q此时的信号量信号将无法发出。线E在处理完共享资源后Q应在离开的同旉过ReleaseSemaphoreQ)函数当前可用资源计数加1。在M时候当前可用资源计数决不可能大于最大资源计数?br> ? 使用信号量对象控制资?br> 下面l合图例3来演CZ号量对象对资源的控制。在?中,以箭头和白色头表示׃n资源所允许的最大资源计数和当前可用资源计数。初始如图(aQ所C,最大资源计数和当前可用资源计数均ؓ(f)4Q此后每增加一个对资源q行讉K的线E(用黑色箭头表C)当前资源计数׃(x)相应?Q图QbQ即表示的在3个线E对׃n资源q行讉K时的状态。当q入U程数达?个时Q将如图QcQ所C,此时已达到最大资源计敎ͼ而当前可用资源计C已减?Q其他线E无法对׃n资源q行讉K。在当前占有资源的线E处理完毕而退出后Q将?x)释攑ևI间Q图QdQ已有两个线E退出对资源的占有,当前可用计数?Q可以再允许2个线E进入到对资源的处理。可以看出,信号量是通过计数来对U程讉K资源q行控制的,而实际上信号量确实也被称作Dijkstra计数器?br> 使用信号量内核对象进行线E同步主要会(x)用到CreateSemaphoreQ)、OpenSemaphoreQ)、ReleaseSemaphoreQ)、WaitForSingleObjectQ)和W(xu)aitForMultipleObjectsQ){函数。其中,CreateSemaphoreQ)用来创徏一个信号量内核对象Q其函数原型为:(x)
HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性指?br> LONG lInitialCount, // 初始计数 LONG lMaximumCount, // 最大计?br> LPCTSTR lpName // 对象名指?br>);
参数l(f)MaximumCount是一个有W号32位|定义了允许的最大资源计敎ͼ最大取g能超q?294967295。lpName参数可以为创建的信号量定义一个名字,׃其创建的是一个内核对象,因此在其他进E中可以通过该名字而得到此信号量。OpenSemaphoreQ)函数卛_用来Ҏ(gu)信号量名打开在其他进E中创徏的信号量Q函数原型如下:(x)
HANDLE OpenSemaphore( DWORD dwDesiredAccess, // 讉K标志 BOOL bInheritHandle, // l承标志 LPCTSTR lpName // 信号量名 );
在线E离开对共享资源的处理Ӟ必须通过ReleaseSemaphoreQ)来增加当前可用资源计数。否则将?x)出现当前正在处理共享资源的实际U程数ƈ没有辑ֈ要限制的数|而其他线E却因ؓ(f)当前可用资源计数?而仍无法q入的情c(din)ReleaseSemaphoreQ)的函数原型ؓ(f)Q?br>
BOOL ReleaseSemaphore( HANDLE hSemaphore, // 信号量句?br> LONG lReleaseCount, // 计数递增数量 LPLONG lpPreviousCount // 先前计数 );
该函数将lReleaseCount中的值添加给信号量的当前资源计数Q一般将lReleaseCount讄?Q如果需要也可以讄其他的倹{WaitForSingleObjectQ)和W(xu)aitForMultipleObjectsQ)主要用在试图q入׃n资源的线E函数入口处Q主要用来判断信号量的当前可用资源计数是否允许本U程的进入。只有在当前可用资源计数值大?Ӟ被监视的信号量内核对象才?x)得到通知?br> 信号量的使用特点使其更适用于对SocketQ套接字Q程序中U程的同步。例如,|络上的HTTP服务器要对同一旉内访问同一面的用h加以限制Q这时可以ؓ(f)没一个用户对服务器的面h讄一个线E,而页面则是待保护的共享资源,通过使用信号量对U程的同步作用可以确保在M时刻无论有多用户对某一面q行讉KQ只有不大于讑֮的最大用h目的U程能够q行讉KQ而其他的讉K企图则被挂vQ只有在有用户退出对此页面的讉K后才有可能进入。下面给出的CZ代码卛_CZcM的处理过E:(x)
// 信号量对象句?br>HANDLE hSemaphore; UINT ThreadProc15(LPVOID pParam) { // 试图q入信号量关?br> WaitForSingleObject(hSemaphore, INFINITE); // U程d处理 AfxMessageBox("U程一正在执行!"); // 释放信号量计?br> ReleaseSemaphore(hSemaphore, 1, NULL); return 0; } UINT ThreadProc16(LPVOID pParam) { // 试图q入信号量关?br> WaitForSingleObject(hSemaphore, INFINITE); // U程d处理 AfxMessageBox("U程二正在执?"); // 释放信号量计?br> ReleaseSemaphore(hSemaphore, 1, NULL); return 0; } UINT ThreadProc17(LPVOID pParam) { // 试图q入信号量关?br> WaitForSingleObject(hSemaphore, INFINITE); // U程d处理 AfxMessageBox("U程三正在执?"); // 释放信号量计?br> ReleaseSemaphore(hSemaphore, 1, NULL); return 0; } …… void CSample08View::OnSemaphore() { // 创徏信号量对?br> hSemaphore = CreateSemaphore(NULL, 2, 2, NULL); // 开启线E?br> AfxBeginThread(ThreadProc15, NULL); AfxBeginThread(ThreadProc16, NULL); AfxBeginThread(ThreadProc17, NULL); }
? 开始进入的两个U程 ? U程二退出后U程三才得以q入 上述代码在开启线E前首先创徏了一个初始计数和最大资源计数均?的信号量对象hSemaphore。即在同一时刻只允?个线E进入由hSemaphore保护的共享资源。随后开启的三个U程均试图访问此׃n资源Q在前两个线E试图访问共享资源时Q由于hSemaphore的当前可用资源计数分别ؓ(f)2?Q此时的hSemaphore是可以得到通知的,也就是说位于U程入口处的WaitForSingleObjectQ)立卌回,而在前两个线E进入到保护区域后,hSemaphore的当前资源计数减到0QhSemaphore不再得到通知QW(xu)aitForSingleObjectQ)线E挂赗直到此前进入到保护区的U程退出后才能得以q入。图4和图5ZqC脉的q行l果。从实验l果可以看出Q信号量始终保持了同一时刻不超q?个线E的q入?br> 在MFC中,通过CSemaphorecd信号量作了表q。该cdh一个构造函敎ͼ可以构造一个信号量对象Qƈ对初始资源计数、最大资源计数、对象名和安全属性等q行初始化,其原型如下:(x)
CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );
在构造了CSemaphorecd象后QQ何一个访问受保护׃n资源的线E都必须通过CSemaphore从父cCSyncObjectcȝ承得到的LockQ)和UnLockQ)成员函数来访问或释放CSemaphore对象。与前面介绍的几U通过MFCcM持线E同步的Ҏ(gu)cMQ通过CSemaphorecM可以前面的U程同步代码q行改写Q这两种使用信号量的U程同步Ҏ(gu)无论是在实现原理上还是从实现l果上都是完全一致的。下面给出经MFC改写后的信号量线E同步代码:(x)
// MFC信号量类对象 CSemaphore g_clsSemaphore(2, 2); UINT ThreadProc24(LPVOID pParam) { // 试图q入信号量关?br> g_clsSemaphore.Lock(); // U程d处理 AfxMessageBox("U程一正在执行!"); // 释放信号量计?br> g_clsSemaphore.Unlock(); return 0; } UINT ThreadProc25(LPVOID pParam) { // 试图q入信号量关?br> g_clsSemaphore.Lock(); // U程d处理 AfxMessageBox("U程二正在执?"); // 释放信号量计?br> g_clsSemaphore.Unlock(); return 0; } UINT ThreadProc26(LPVOID pParam) { // 试图q入信号量关?br> g_clsSemaphore.Lock(); // U程d处理 AfxMessageBox("U程三正在执?"); // 释放信号量计?br> g_clsSemaphore.Unlock(); return 0; } …… void CSample08View::OnSemaphoreMfc() { // 开启线E?br> AfxBeginThread(ThreadProc24, NULL); AfxBeginThread(ThreadProc25, NULL); AfxBeginThread(ThreadProc26, NULL); }
互斥内核对象 互斥QMutexQ是一U用途非常广泛的内核对象。能够保证多个线E对同一׃n资源的互斥访问。同临界区有些类|只有拥有互斥对象的线E才h讉K资源的权限,׃互斥对象只有一个,因此决定了M情况下此׃n资源都不?x)同时被多个U程所讉K。当前占据资源的U程在Q务处理完后应拥有的互斥对象交出Q以便其他线E在获得后得以访问资源。与其他几种内核对象不同Q互斥对象在操作pȝ中拥有特D代码,q由操作pȝ来管理,操作pȝ甚至q允许其q行一些其他内核对象所不能q行的非常规操作。ؓ(f)便于理解Q可参照?l出的互斥内核对象的工作模型Q?br> ? 使用互斥内核对象对共享资源的保护 图(aQ中的箭头ؓ(f)要访问资源(矩Ş框)的线E,但只有第二个U程拥有互斥对象Q黑点)q得以进入到׃n资源Q而其他线E则?x)被排斥在外Q如图(bQ所C)。当此线E处理完׃n资源q准备离开此区域时把其所拥有的互斥对象交出(如图QcQ所C)Q其他Q何一个试图访问此资源的线E都有机?x)得到此互斥对象?br> 以互斥内核对象来保持U程同步可能用到的函C要有CreateMutexQ)、OpenMutexQ)、ReleaseMutexQ)、WaitForSingleObjectQ)和W(xu)aitForMultipleObjectsQ){。在使用互斥对象前,首先要通过CreateMutexQ)或OpenMutexQ)创徏或打开一个互斥对象。CreateMutexQ)函数原型为:(x)
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全属性指?br> BOOL bInitialOwner, // 初始拥有?br> LPCTSTR lpName // 互斥对象?br>);
参数bInitialOwner主要用来控制互斥对象的初始状态。一般多其讄为FALSEQ以表明互斥对象在创建时q没有ؓ(f)MU程所占有。如果在创徏互斥对象时指定了对象名,那么可以在本q程其他地方或是在其他进E通过OpenMutexQ)函数得到此互斥对象的句柄。OpenMutexQ)函数原型为:(x)
HANDLE OpenMutex( DWORD dwDesiredAccess, // 讉K标志 BOOL bInheritHandle, // l承标志 LPCTSTR lpName // 互斥对象?br>);
当目前对资源h讉K权的U程不再需要访问此资源而要dӞ必须通过ReleaseMutexQ)函数来释攑օ拥有的互斥对象,其函数原型ؓ(f)Q?br>
BOOL ReleaseMutex(HANDLE hMutex);
其唯一的参数hMutex为待释放的互斥对象句柄。至于WaitForSingleObjectQ)和W(xu)aitForMultipleObjectsQ){待函数在互斥对象保持线E同步中所L(fng)作用与在其他内核对象中的作用是基本一致的Q也是等待互斥内核对象的通知。但是这里需要特别指出的是:(x)在互斥对象通知引v调用{待函数q回Ӟ{待函数的返回g再是通常的WAIT_OBJECT_0Q对于WaitForSingleObjectQ)函数Q或是在WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1之间的一个|对于WaitForMultipleObjectsQ)函数Q,而是返回一个WAIT_ABANDONED_0Q对于WaitForSingleObjectQ)函数Q或是在WAIT_ABANDONED_0到WAIT_ABANDONED_0+nCount-1之间的一个|对于WaitForMultipleObjectsQ)函数Q。以此来表明U程正在{待的互斥对象由另外一个线E所拥有Q而此U程却在使用完共享资源前已l终止。除此之外,使用互斥对象的方法在{待U程的可调度性上同用其他几U内核对象的Ҏ(gu)也有所不同Q其他内核对象在没有得到通知Ӟ受调用等待函数的作用Q线E将?x)挂P同时失去可调度性,而用互斥的Ҏ(gu)却可以在{待的同时仍h可调度性,q也正是互斥对象所能完成的非常规操作之一?br> 在编写程序时Q互斥对象多用在寚w些ؓ(f)多个U程所讉K的内存块的保护上Q可以确保Q何线E在处理此内存块旉对其拥有可靠的独占访问权。下面给出的CZ代码即通过互斥内核对象hMutex对共享内存快g_cArray[]q行U程的独占访问保护。下面给出实C码清单:(x)
// 互斥对象 HANDLE hMutex = NULL; char g_cArray[10]; UINT ThreadProc18(LPVOID pParam) { // {待互斥对象通知 WaitForSingleObject(hMutex, INFINITE); // 对共享资源进行写入操?br> for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // 释放互斥对象 ReleaseMutex(hMutex); return 0; } UINT ThreadProc19(LPVOID pParam) { // {待互斥对象通知 WaitForSingleObject(hMutex, INFINITE); // 对共享资源进行写入操?br> for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } // 释放互斥对象 ReleaseMutex(hMutex); return 0; } …… void CSample08View::OnMutex() { // 创徏互斥对象 hMutex = CreateMutex(NULL, FALSE, NULL); // 启动U程 AfxBeginThread(ThreadProc18, NULL); AfxBeginThread(ThreadProc19, NULL); // {待计算完毕 Sleep(300); // 报告计算l果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); }
互斥对象在MFC中通过CMutexc进行表q。用CMutexcȝҎ(gu)非常单,在构造CMutexcd象的同时可以指明待查询的互斥对象的名字,在构造函数返回后卛_讉K此互斥变量。CMutexcM是只含有构造函数这唯一的成员函敎ͼ当完成对互斥对象保护资源的访问后Q可通过调用从父cCSyncObjectl承的UnLockQ)函数完成对互斥对象的释放。CMutexcL造函数原型ؓ(f)Q?br>
CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );
该类的适用范围和实现原理与API方式创徏的互斥内核对象是完全cM的,但要z的多,下面l出是对前面的CZ代码lCMutexcL写后的程序实现清单:(x)
// MFC互斥cd?br>CMutex g_clsMutex(FALSE, NULL); UINT ThreadProc27(LPVOID pParam) { // {待互斥对象通知 g_clsMutex.Lock(); // 对共享资源进行写入操?br> for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // 释放互斥对象 g_clsMutex.Unlock(); return 0; } UINT ThreadProc28(LPVOID pParam) { // {待互斥对象通知 g_clsMutex.Lock(); // 对共享资源进行写入操?br> for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } // 释放互斥对象 g_clsMutex.Unlock(); return 0; } …… void CSample08View::OnMutexMfc() { // 启动U程 AfxBeginThread(ThreadProc27, NULL); AfxBeginThread(ThreadProc28, NULL); // {待计算完毕 Sleep(300); // 报告计算l果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); }
结 U程的用ɽE序处理更够更加灉|Q而这U灵zd样也?x)带来各U不定性的可能。尤其是在多个线E对同一公共变量q行讉K时。虽然未使用U程同步的程序代码在逻辑上或许没有什么问题,但ؓ(f)了确保程序的正确、可靠运行,必须在适当的场合采取线E同步措施?/strong>
]]>
ĻѾþ |
˾þһþ |
ѾƷպȾþ |
þҹӰ |
þˬˬAV |
þƷĻ |
999þþѾƷ |
þۺɫHEZYO |
þþƷˬӰ |
þþþþaŷa |
Ҫþðѹۿ |
ƷŮٸaѾþ |
þþƷŷպ |
þ㽶97Ʒ |
ŷaƬѿþ |
þþƷav |
þùһ |
þþƷav |
˾þۺһ77 |
þùɫAVѿ |
þþ |
þþۺ㽶ۺ |
þþþø߳ëƬȫ |
þӰԺҹƬ |
þþƷվ |
˾þþƷavһ |
þþþþྫƷֱ |
91þþһȫ |
ƷþþĻ |
þۺϾɫŷۺϺݺ |
Ʒþþþ |
˸ŮѲžþþ |
ձŷþþþѲ
|
þþþ |
91ƷۺϾþĻþһ
|
Ʒþþþþר
|
ھƷþþþþþþõӰ
|
99þþƷѿ |
ھƷžžþþþƷ |
þþƷAV鶹վ |
vaþþþ |