EnterCriticalSection 多线程操作相同数据遇到的问题(线程锁)

在编程中常遇到这样的问题,线程A、B都需要用到数据Data。如线程A是用于写数据(Write)而线程B是用于读数据(Read)。一般而言这两个线程是互斥的,要不就读要不就是写。如果A线程还在运行时,B线程突然醒来,那么必然出现Data文件被占用的情况。为了避免这样的事情,我们就需要对数据进行“保护”。即在它被某个线程占用的时候,另外一个线程是不能访问它的,可以理解成让线程排队等候。EnterCriticalSection LeaveCriticalSection就是现在要讲的函数,它们能做到以上所说的要求。

函数 EnterCriticalSection 声明如下:

WINBASEAPI

VOID

WINAPI

EnterCriticalSection(

__inoutLPCRITICAL_SECTION lpCriticalSection

     );

LeaveCriticalSection类似,更简单的说这两个函数是用来确保同一时刻,只有一个线程对被保护的数据进行操作。相关的多线程数据函数还有

  InitializeCriticalSection(&cs);//初始化临界区           

  EnterCriticalSection(&cs);//进入临界区

//操作数据()

MyMoney = 10;//所有访问MytMoney变量的进程都需要这样些Enter.. Leave...

LeaveCriticalSection(&cs);//离开临界区

DeleteCriticalSection(&cs);//删除临界区

比如说我们定义了一个共享资源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,就以此类推。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中,可以使用Windows线程锁来实现线程同步和互斥。Windows提供了几种不同的线程锁类型,其中最常用的是互斥量(Mutex)和临界区(Critical Section)。 互斥量是一种二进制信号量,用于保护共享资源。在使用互斥量时,只允许一个线程访问被保护的资源,其他线程需要等待互斥量释放后才能访问。互斥量的使用可以通过以下步骤实现: 1. 创建互斥量对象:使用CreateMutex函数创建一个互斥量对象,返回一个句柄。 ```cpp HANDLE hMutex = CreateMutex(NULL, FALSE, NULL); ``` 2. 请求互斥量:在需要保护共享资源的地方,使用WaitForSingleObject函数请求互斥量。 ```cpp WaitForSingleObject(hMutex, INFINITE); ``` 3. 访问共享资源:在获得互斥量后,进行对共享资源的访问。 4. 释放互斥量:使用ReleaseMutex函数释放互斥量。 ```cpp ReleaseMutex(hMutex); ``` 临界区是一种更轻量级的线程同步对象,类似于互斥量。它也只允许一个线程访问共享资源。使用临界区的步骤如下: 1. 声明临界区:在需要使用临界区的地方,声明一个CRITICAL_SECTION对象。 ```cpp CRITICAL_SECTION cs; InitializeCriticalSection(&cs); ``` 2. 进入临界区:使用EnterCriticalSection函数进入临界区。 ```cpp EnterCriticalSection(&cs); ``` 3. 访问共享资源:在获得临界区后,进行对共享资源的访问。 4. 离开临界区:使用LeaveCriticalSection函数离开临界区。 ```cpp LeaveCriticalSection(&cs); ``` 需要注意的是,使用完互斥量和临界区后,需要分别调用CloseHandle和DeleteCriticalSection来释放资源。 以上是C++中使用Windows线程锁的基本操作。当多个线程需要共享资源时,使用线程锁可以保证数据的一致性和避免竞争条件的发生。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值