`
mmdev
  • 浏览: 13436109 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

理解EnterCriticalSection 临界区

 
阅读更多

通俗解释就像上厕所:
门锁了,就等着,等到别人出来了,进去锁上,然后该干什么干什么,干完了,把门打开

门没锁,就进去,锁上,然后该干什么干什么,干完了,把门打开

--------------------------------------------------
多线程中用来确保同一时刻只有一个线程操作被保护的数据

InitializeCriticalSection(&cs);//初始化临界区
EnterCriticalSection(&cs);//进入临界区
//操作数据
MyMoney*=10;//所有访问MyMoney变量的程序都需要这样写Enter.. Leave...
LeaveCriticalSection(&cs);//离开临界区
DeleteCriticalSection(&cs);//删除临界区

多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,无法控制数据,变成随机变量。为解决这个问题,就需要引入互斥变量,让每个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和LeaveCriticalSection函数。

比如说我们定义了一个共享资源dwTime[100],两个线程ThreadFuncA和ThreadFuncB都对它进行读写操作。当我们想要保证 dwTime[100]的操作完整性,即不希望写到一半的数据被另一个线程读取,那么用CRITICAL_SECTION来进行线程同步如下:

第一个线程函数:

DWORD WINAPI ThreadFuncA(LPVOID lp)
{
EnterCriticalSection(&cs);
...
// 操作dwTime
...
LeaveCriticalSection(&cs);
return 0;
}

写出这个函数之后,很多初学者都会错误地以为,此时cs对dwTime进行了锁定操作,dwTime处于cs的保护之中。一个“自然而然”的想法就是——cs和dwTime一一对应上了。

这么想,就大错特错了。dwTime并没有和任何东西对应,它仍然是任何其它线程都可以访问的。如果你像如下的方式来写第二个线程,那么就会有问题:

DWORD WINAPI ThreadFuncB(LPVOID lp)
{
...
// 操作dwTime
...
return 0;
}

当线程ThreadFuncA执行了EnterCriticalSection(&cs),并开始操作dwTime[100]的时候,线程 ThreadFuncB可能随时醒过来,也开始操作dwTime[100],这样,dwTime[100]中的数据就被破坏了。

为了让CRITICAL_SECTION发挥作用,我们必须在访问dwTime的任何一个地方都加上 EnterCriticalSection(&cs)和LeaveCriticalSection(&cs)语句。所以,必须按照下面的 方式来写第二个线程函数:

DWORD WINAPI ThreadFuncB(LPVOID lp)
{
EnterCriticalSection(&cs);
...
// 操作dwTime
...
LeaveCriticalSection(&cs);
return 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,就以此类推。


通俗解释就像上厕所:
门锁了,就等着,等到别人出来了,进去锁上,然后该干什么干什么,干完了,把门打开

门没锁,就进去,锁上,然后该干什么干什么,干完了,把门打开

--------------------------------------------------
多线程中用来确保同一时刻只有一个线程操作被保护的数据

InitializeCriticalSection(&cs);//初始化临界区
EnterCriticalSection(&cs);//进入临界区
//操作数据
MyMoney*=10;//所有访问MyMoney变量的程序都需要这样写Enter.. Leave...
LeaveCriticalSection(&cs);//离开临界区
DeleteCriticalSection(&cs);//删除临界区

多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,无法控制数据,变成随机变量。为解决这个问题,就需要引入互斥变量,让每个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和LeaveCriticalSection函数。

比如说我们定义了一个共享资源dwTime[100],两个线程ThreadFuncA和ThreadFuncB都对它进行读写操作。当我们想要保证 dwTime[100]的操作完整性,即不希望写到一半的数据被另一个线程读取,那么用CRITICAL_SECTION来进行线程同步如下:

第一个线程函数:

DWORD WINAPI ThreadFuncA(LPVOID lp)
{
EnterCriticalSection(&cs);
...
// 操作dwTime
...
LeaveCriticalSection(&cs);
return 0;
}

写出这个函数之后,很多初学者都会错误地以为,此时cs对dwTime进行了锁定操作,dwTime处于cs的保护之中。一个“自然而然”的想法就是——cs和dwTime一一对应上了。

这么想,就大错特错了。dwTime并没有和任何东西对应,它仍然是任何其它线程都可以访问的。如果你像如下的方式来写第二个线程,那么就会有问题:

DWORD WINAPI ThreadFuncB(LPVOID lp)
{
...
// 操作dwTime
...
return 0;
}

当线程ThreadFuncA执行了EnterCriticalSection(&cs),并开始操作dwTime[100]的时候,线程 ThreadFuncB可能随时醒过来,也开始操作dwTime[100],这样,dwTime[100]中的数据就被破坏了。

为了让CRITICAL_SECTION发挥作用,我们必须在访问dwTime的任何一个地方都加上 EnterCriticalSection(&cs)和LeaveCriticalSection(&cs)语句。所以,必须按照下面的 方式来写第二个线程函数:

DWORD WINAPI ThreadFuncB(LPVOID lp)
{
EnterCriticalSection(&cs);
...
// 操作dwTime
...
LeaveCriticalSection(&cs);
return 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,就以此类推。

分享到:
评论

相关推荐

    一个临界区类

    2. `EnterCriticalSection()`:让当前线程进入临界区。如果临界区已经由另一个线程占有,那么调用线程将被阻塞,直到临界区变得可用。 3. `LeaveCriticalSection()`:让当前线程离开临界区。这个函数应该在线程完成...

    Critical(VC2010条件下通过临界区实现多线程同步)

    调用`EnterCriticalSection`进入临界区,`LeaveCriticalSection`退出临界区。当一个线程在临界区内时,其他尝试进入的线程会被阻塞,直到当前线程离开临界区。 3. 最后,记得在不再需要临界区时进行清理: ```cpp ...

    Examples_critical_region.rar_critical_临界区_线程临界区

    2. 进入临界区:使用`EnterCriticalSection()`函数,线程请求进入临界区。如果当前没有其他线程在临界区内,该线程将成功进入并开始执行临界区内的代码。如果有其他线程已经进入,那么当前线程会被挂起,直到其他...

    利用临界区同步线程例子

    首先,我们需要理解临界区的概念。临界区是一个代码段,其中包含对共享资源的访问。当一个线程进入临界区时,其他试图进入的线程将被阻塞,直到该线程离开临界区。在Delphi中,我们可以使用TCriticalSection对象来...

    多线程同步(多线程如何访问临界区资源)

    在Windows API中,可以使用`EnterCriticalSection`和`LeaveCriticalSection`函数来进入和退出临界区。这些函数会自动处理线程间的同步,确保一次只有一个线程能执行临界区内的代码。在C++标准库中,也有`std::mutex`...

    ljq.rar_线程临界区

    在多线程编程中,确保线程安全是至关重要的,而线程临界区就是解决这一问题的关键技术之一。...通过理解和熟练运用线程临界区,开发者可以在VC++的多线程环境中构建出高效、可靠的程序,确保数据一致性,避免并发问题。

    critical-section.gz_critical_临界区_线程临界区

    这个主题的核心是理解和实现线程临界区的有效管理,以确保系统的正确性和高效性。 在多线程编程中,多个线程可能同时运行,如果它们尝试修改同一份共享数据,可能会导致混乱的结果。例如,两个线程同时增加一个...

    CriticalSection.rar_CriticalSection_线程临界区

    2. **进入临界区**:在需要保护的代码块之前,线程通过调用`EnterCriticalSection()`函数进入临界区。此时,其他尝试进入临界区的线程将被阻塞,直到当前线程退出临界区。 3. **执行关键操作**:在临界区内,线程...

    利用临界区的多线程同步测试.rar_临界区_多线程同步_线程 同步_线程同步

    临界区的实现通常依赖于操作系统提供的原语,如Windows下的`EnterCriticalSection`和`LeaveCriticalSection`,或者POSIX标准中的`pthread_mutex_lock`和`pthread_mutex_unlock`。这些函数使得线程在进入临界区前需要...

    精选_线程同步之临界区_源码打包

    通过理解临界区的工作原理,并结合提供的"criticalsection_test"源码,开发者可以深入学习如何在实际项目中有效地实现线程同步,确保多线程程序的稳定和正确运行。同时,这也是一种提高软件并发性能和可维护性的基础...

    使用临界区启动线程,VC源代码下载.rar

    首先,理解临界区的概念是关键。临界区是一种同步原语,通过定义一个代码段,当一个线程正在执行该代码段时,其他试图进入的线程会被挂起,直到第一个线程完成并离开。这样可以防止多个线程同时修改同一数据,确保...

    pthread和win32的临界区 Critical Section 比较

    在Windows中,临界区通过`EnterCriticalSection`和`LeaveCriticalSection`函数来进入和退出临界区;而在POSIX线程库pthread中,临界区则通过`pthread_mutex_lock`和`pthread_mutex_unlock`来实现。 在Win32 API中,...

    c++使用临界区来互斥使用资源,比如多线程

    首先,让我们理解临界区的概念。临界区通过使用特定的同步原语(如互斥量、信号量或事件)来实现。在C++中,我们可以使用`#include <windows.h>`引入Windows API,然后定义一个`CRITICAL_SECTION`结构体来表示临界区...

    VC多线程例程七及图解文档(使用临界区机制进行线程同步)

    3. **使用临界区进行同步**:在VC++中,可以使用`CRITICAL_SECTION`结构体来定义临界区,并通过`InitializeCriticalSection`初始化,`EnterCriticalSection`和`LeaveCriticalSection`来管理。这样可以确保同一时间...

    临界区的互斥控制(2).pdf

    在实际使用时,可以根据需求选择事件、互斥量或临界区进行同步,并在代码中适当的位置调用`WaitForSingleObject`或`EnterCriticalSection`等函数来确保线程安全。 总结来说,临界区的互斥控制是多线程编程中防止...

    Delphi线程同步(临界区、互斥、信号量).pdf

    本文将重点介绍三种常见的同步机制:临界区、互斥量和信号量,并通过示例代码帮助理解它们的工作原理及应用场景。 #### 二、临界区(Critical Section) 临界区是最简单的线程同步方式之一,适用于同一进程中的...

    临界区(Critical Section)的封装和使用示例

    在Windows操作系统中,我们可以使用API函数如`EnterCriticalSection`和`LeaveCriticalSection`来实现临界区。而在C++中,我们可以使用互斥量(mutex)或者C++11标准库中的`std::mutex`来实现类似的功能。 下面是一...

    临界线程同步

    如果已有其他线程在临界区内,调用`EnterCriticalSection`的线程会被挂起,直到其他线程退出临界区。 3. **离开临界区**: 当线程完成其任务并准备离开临界区时,使用`LeaveCriticalSection`函数。这将释放临界区,...

    Windows临界区,内核事件,互斥量,信号量.pdf

    - `EnterCriticalSection`:进入临界区,阻止其他线程进入。 - `LeaveCriticalSection`:离开临界区,允许其他线程进入。 - `DeleteCriticalSection`:删除临界区结构体,释放资源。 ### 内核对象 - 事件(Event...

    一看就懂,一做就会:临界资源互斥

    例如,我们创建一个对话框类`CriticalSectionDlg`,在这个类中我们可以定义临界区对象,然后在需要保护的代码段前后分别调用`EnterCriticalSection`和`LeaveCriticalSection`。 以下是一个简单的示例: ```cpp #...

Global site tag (gtag.js) - Google Analytics