被自己的愚蠢与鲁莽坑了无数次后,开始明白一个道理。不管多灵异的bug,都不会是复杂到让你无法理解的。至少大多数时候是这样的……
CRITICAL_SECTION是Windows下定义的一个结构体,作用就是字面意思,临界区。
这个临界区指的是在 EnterCriticalSection与LeaveCriticalSection中间的这一段代码为临界区。比如你有好几个线程都要使用几个临界资源,可以把对临界资源的全部操作都放入临界区中,使线程同步。
注意:一定要把所有对临界资源的操作都要放入临界区域内。否则很有可能会造成各种奇怪的死锁。
CRITICAL_SECTION的作用其实并不是锁定资源,而是“锁定代码”。即不同的线程只能有一个在CRITICAL_SECTION中,不可重入。当已经有其他线程EnterCriticalSection时,当前线程必须等待,直到该线程LeaveCriticalSection。
比如:
thread1()
{
EnterCriticalSection(&lock);
//do nothing
//Sleep(10000);
LeaveCriticalSection(&lock);
}
thread2()
{
EnterCriticalSection(&lock);
//do something
……
LeaveCriticalSection(&lock);
}
当线程一进入临界区时什么都不做,并不操作任何临界资源。线程二依然要等线程离开临界区才能进入临界区操作临界资源。因为CRITICAL_SECTION的作用其实并不是锁定资源,而是“锁定代码”。
今天调试出了一个“历史遗留BUG”,老板的项目中有三个以下三个线程要进行同步。
线程一:从照相机中读取数据放入buffer中。
线程二:从buffer中读取数据并显示。
线程三:从buffer中读取数据做计算处理。(因为要求实时显示,线程三中的数据处理没有那么快的速度,所以干脆分成两个线程)。
功能一:仅仅显示数据,而不做数据处理,即线程一与线程二同步。这个时候程序完全没有问题显示的很好。
功能二:显示并处理数据,三线程进行同步。这个时候也貌似没有问题。
然后再使用功能一,发现死锁了。在线程二进入临界区时无限等待。
重新使用功能二,依然没有问题,只是不能再使用功能一了。
之前总感觉非常的诡异,总以为是线程二的实现出了什么问题。今天仔细的把线程三的实现好好的检查了一遍,发现读取buffer的语句竟然没有放在EnterCriticalSection与LeaveCriticalSection之间,真不知道当时写下这段代码的时候在想什么……
虽然找到问题的所在了,但是对于问题是如何发生的还是理解的不够清晰。以后想清楚了具体是怎么回事再更新吧,反正现在是真的了解了对临界资源的操作一定要放在临界区中!不然真的后果很惨痛。