critical临界的,EnterCriticalSection
通俗的解释就像上厕所:
门是锁着的,就等着,等别人出来了,进去锁上,然后做你该做的,做完了就开门。
如果门没锁,就进去,锁上,然后做你想做的事。完事后,打开门。
-
多线程用于确保同一时间只有一个线程操作受保护的数据。
InitializeCriticalSection(cs);//初始化临界区
enter critical section(cs);//进入临界区
//运行数据
MyMoney *=10//所有访问MyMoney变量的程序都需要写Enter.像这样离开。
LeaveCriticalSection(cs);//离开临界区
DeleteCriticalSection(cs);//删除关键部分
多线程操作同一数据时,一般需要按顺序访问,否则会导致数据无序、数据不可控、随机变量。为了解决这个问题,我们需要引入互斥变量,这样每个线程都可以顺序访问变量。这样,您需要使用EnterCriticalSection和LeaveCriticalSection函数。
比如我们定义一个共享资源dwTime[100],线程ThreadFuncA和ThreadFuncB都对其进行读写。当我们想要保证dwTime[100]的操作完整性,也就是不希望写入的数据有一半被另一个线程读取,那么我们使用CRITICAL_SECTION进行线程同步,如下所示:
第一个线程函数:
DWORD WINAPI thread funca(LPVOID LP)
{
enter critical section(cs);
.
//运行dwTime
.
LeaveCriticalSection(cs);
返回0;
}
写了这个函数后,很多初学者会误以为此时cs已经锁定了dwTime,dwTime处于cs的保护之下。一个“自然”的想法是——cs和dwTime一一对应。
这样想就大错特错了。DW不对应任何东西,它仍然可以被任何其他线程访问。如果你按如下方式编写第二个线程,那么就会出现问题:
DWORD WINAPI thread funcb(LPVOID LP)
{
.
//运行dwTime
.
返回0;
}
当线程ThreadFuncA执行EnterCriticalSection( cs)并开始运行dwTime[100]时,线程ThreadFuncB随时可能被唤醒并开始运行dwTime[100],这样dwTime[100]中的数据就被破坏了。
为了使CRITICAL_SECTION工作,我们必须在访问dwTime的任何地方添加语句EnterCriticalSection( cs)和LeaveCriticalSection( cs)。因此,第二个线程函数必须编写如下:
DWORD WINAPI thread funcb(LPVOID LP)
{
enter critical section(cs);
.
//运行dwTime
.
LeaveCriticalSection(cs);
返回0;
}
这样,当ThreadFuncB醒来时,它遇到的第一条语句是EnterCriticalSection( cs),它将访问cs变量。如果第一个线程此时仍在运行dwTime[100],cs变量中包含的值将告诉第二个线程另一个线程已经占用了cs。因此,第二个线程的EnterCriticalSection( cs)语句将不会返回,而是处于挂起等待状态。在第一个线程执行LeaveCriticalSection( cs)之前,第二个线程的EnterCriticalSection( cs)语句不会返回,后面的操作会继续。
事实上,这个过程通过限制只有一个函数进入CriticalSection变量来实现代码段同步。简单来说,对于同一个CRITICAL_SECTION,当一个线程执行了EnterCriticalSection但没有执行LeaveCriticalSection时,任何其他线程都无法完全执行EnterCriticalSection,只能等待。
同样,没有资源被“锁定”。CRITICAL_SECTION不是针对资源,而是针对不同线程之间的代码段!我们可以用它来“锁”所谓的资源,其实是因为我们在任何访问共享资源的地方都添加了EnterCriticalSection和LeaveCriticalSection的语句,这样同一时间只有一个线程的代码段可以访问共享资源(其他想访问资源的代码段还得等)。
这就是使用CRITICAL_SECTION时发生的情况。要知道它没有可以同步的资源“集合”。这种观念是不正确的。
如果是两个CRITICAL_SECTION,依此类推。