编译原理、计算机原理的基本内容,我不想重复了。放上些内容,主要是关于理论与实际间的差距。
- 线程间共享变量应该在声明时加入volatile关键字
如果不加会怎样?看以下代码:
intptr_t a = 1; // Reset by another thread.
...
while(a)
{// Looping until a equal to zero.
Sleep(0);
}
优化后可能变成:
intptr_t a = 1; // Reset by another thread.
...
register intptr_t xa = a;
while(xa)
{ // Looping until a equals to zero.
Sleep(0);
}
结果在发布版中,程序运行到这里后就死循环。
- 关于“不需要同步”的线程间访问编程
例如你想在一个线程里拷贝文件,而在另一个线程里获取已拷贝的字节数,从而得到进度。
不要以为你将共享变量加上volatile关键字就完事了。这里不想多说,有现成的文章:http://msdn.microsoft.com/en-us/library/ms686355(VS.85).aspx,(你在自己的MSDN光盘上也可找到)
否则在多处理器、多核系统上运行,拷贝文件的进度条直到完成拷贝,还可能停留在0%位置。
- 使用互斥、信号、事件同步对象时调用WaitForSingleObject/WaitForMultipleObject等等API时,还有进入同步保护区时,API里面做了内存分配动作。这样你就应当想到不要假定只有要么等待或超时、要么通过的几种状态。还应考虑申请失败的可能。采用纠错法编码吧。如果你很介意,不希望某个地方出现同步失败,就应该用InterlockedXXX APIs(自旋锁原子操作),创建自己的同步锁(注意:该方法不适用于解决跨进程的线程同步问题)。但要注意锁让cpu进入忙等待,不像waiters那样是让线程挂起并空闲,利用中断来唤醒自己的。
- 不要以为只有你在编译原理里面学过的几种同步源,事实上,在Windows中,堆内存管理、设备、窗口消息、事件等等很多都事先考虑了同步访问问题。例如,假如你从窗口线程外用SendMessage向窗口发消息时,就不用考虑窗口资源是否可能被从不同的线程同时访问的问题。因为窗口管理程序已经将所有请求串行化了。
--------------------------------------------------------------------------------------------------------------------
可以避免申请失败的关键区(EnterCriticalSection)替代方案之一:
#ifndef _VOLATILE_SPECIALIZED_EXCHGPTR #define _VOLATILE_SPECIALIZED_EXCHGPTR #endif template < int _WAIT_CPUIDLE_ = 1 > class CGlobalFastMutex : public CGlobalFastMutex<-1> { public: CGlobalFastMutex(tagFastMutexCnt & obj) { DWORD dwThrIdRet, dwThrId = GetCurrentThreadId(); static_assert( _WAIT_CPUIDLE_ >= 0 ); for(;;) { dwThrIdRet = (DWORD)(size_t)InterlockedCompareExchangePointer(obj.GetEntry(), (void *)(size_t)dwThrId, NULL); if(!dwThrIdRet || dwThrId == dwThrIdRet) { obj.cEntered++, m_pHook = &obj; break; } Sleep( _WAIT_CPUIDLE_ ); } }; private: CGlobalFastMutex(const CGlobalFastMutex &); CGlobalFastMutex & operator = (const CGlobalFastMutex &); }; template <> class CGlobalFastMutex<-1> { public: struct tagFastMutexCnt { friend class CGlobalFastMutex; private: void * volatile vReserved; void * volatile vSynVal; volatile size_t cEntered; public: tagFastMutexCnt(void): cEntered(0) { #pragma warning(push) #pragma warning(disable: 4312) InterlockedExchangePointer(_VOLATILE_SPECIALIZED_EXCHGPTR GetEntry(), NULL); #pragma warning(pop) }; private: void * volatile * GetEntry(void) { return((void * volatile *)((size_t)&vSynVal / sizeof(void *) * sizeof(void *))); }; tagFastMutexCnt(const tagFastMutexCnt &); tagFastMutexCnt & operator = (const tagFastMutexCnt &); }; CGlobalFastMutex(tagFastMutexCnt & obj): m_pHook(NULL) { DWORD dwThrIdRet, dwThrId = GetCurrentThreadId(); dwThrIdRet = (DWORD)(size_t)InterlockedCompareExchangePointer(obj.GetEntry(), (void *)(size_t)dwThrId, NULL); if(!dwThrIdRet || dwThrId == dwThrIdRet) obj.cEntered++, m_pHook = &obj; }; ~CGlobalFastMutex(void) { if(m_pHook) { m_pHook->cEntered--; if(m_pHook->cEntered) return; #pragma warning(push) #pragma warning(disable: 4312) InterlockedExchangePointer(_VOLATILE_SPECIALIZED_EXCHGPTR m_pHook->GetEntry(), NULL); #pragma warning(pop) } }; operator bool (void) { return(m_pHook != NULL); }; private: CGlobalFastMutex(const CGlobalFastMutex &); CGlobalFastMutex & operator = (const CGlobalFastMutex &); tagFastMutexCnt * m_pHook; protected: CGlobalFastMutex(void): m_pHook(NULL) {}; };