欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Windows线程同步之临界区对象(Critical Section)

程序员文章站 2022-07-05 10:36:17
...

我们也可以通过临界区保证在同一时间内只有一个线程对共享数据进行控制访问。临界区不是内核对象,只能对进程内部的线程进行同步。

临界区对象是定义在数据段中的一个CRITICAL_SECTION结构,Windows内部使用这个结构记录的一些信息,来确保同一个时间只有一个线程访问该临界区保护的数据。

临界区对象使用要调用的函数接口如下:

1InitializeCriticalSection()

void WINAPI InitializeCriticalSection(
  _Out_  LPCRITICAL_SECTION lpCriticalSection
);

//lpCriticalSection:指向临界区对象的指针

创建CRITICAL_SECTION对象后,需要调用该函数进行临界区对象的初始化。任何线程在试图访问临界区所保护的资源之前,CRITICAL_SECTION结构对象的内部成员必须已经初始化。如果线程试图进入一个未经初始化的CRITICAL_SECTION,那么结果是不可预料的。

2EnterCriticalSection()

void WINAPI EnterCriticalSection(
  _Inout_  LPCRITICAL_SECTION lpCriticalSection
);
//lpCriticalSection:指向临界区对象的指针

当线程要访问临界区所保护的数据的时候,必须首先调用该函数进入临界区,在同一时间,Windows只运行有一个线程进入临界区,如果调用EnterCriticalSection时,已经有线程在临界区内,那么会调用一个事件内核对象把调用线程切换到等待状态,这样调用线程不会浪费任何CPU时间,系统内核会记住这个线程想要访问的资源,当该资源可用时(另一个使用该资源的线程调用LeaveCriticalSection),系统会将等待中的线程切换回可调度的状态。

3TryEnterCriticalSection()

BOOL WINAPI TryEnterCriticalSection(
  _Inout_  LPCRITICAL_SECTION lpCriticalSection
);

//lpCriticalSection:指向临界区对象的指针
//Return Value:表示是否进入临界区

TryEnterCriticalSectionEnterCriticalSection函数唯一的区别就是调用线程不会进入等待状态。如果共享资源真在被其他线程访问,那么该函数会返回FALSE,表示进入临界区失败,如果返回TRUE,表示已经进入临界区。

4LeaveCriticalSection()

void WINAPI LeaveCriticalSection(
  _Inout_  LPCRITICAL_SECTION lpCriticalSection
);

//lpCriticalSection:指向临界区对象的指针

当线程完成了对共享资源的访问,应该调用该函数退出临界区,以便其他线程可以进行访问。

5DeleteCriticalSection()

void WINAPI DeleteCriticalSection(
  _Inout_  LPCRITICAL_SECTION lpCriticalSection
);

//lpCriticalSection:指向临界区对象的指针

当程序不再使用临界区的时候,必须调用该函数将临界区对象删除。

使用临界区对象时有两点要注意:

  1. 任何访问共享资源的代码都应该放在EnterCriticalSectionLeaveCriticalSection之间,也就是这两个函数要成对出现,否则共享资源就有可能被破坏。
  2. 不要在临界区内运行较长时间,这样会影响程序的性能。

下面程序测试临界区功能:


#include <iostream>
#include <windows.h>
#include <process.h>

using namespace  std;

CRITICAL_SECTION csTest;
volatile int flag = 0;

void CriticalSectionTest1(void *ptr)
{
    EnterCriticalSection(&csTest);

    flag = 1;
    for (int i = 0; i < 5; ++i)
    {
        Sleep(1000);
        cout<<flag<<' ';
    }

    LeaveCriticalSection(&csTest);
}

void CriticalSectionTest2(void *ptr)
{
    EnterCriticalSection(&csTest);

    flag = 2;
    for (int i = 0; i < 5; ++i)
    {
        Sleep(1000);
        cout<<flag<<' ';
    }
    LeaveCriticalSection(&csTest);
}

int main()
{
    InitializeCriticalSection(&csTest);

   _beginthread(CriticalSectionTest1, 0, NULL);
    _beginthread(CriticalSectionTest2, 0, NULL);
    
    Sleep(INFINITE);

    DeleteCriticalSection(&csTest);
}

程序运行结果:

2 2 2 2 2 1 1 1 1 1
这里从运行结果可以看出,两个线程互斥的访问临界区资源,这里为了测试在进入临界区后调用Sleep函数, 但在实际程序中这种行为会严重影响程序的性能。


Jun 19, 2013 PM 22:58 @dorm