C++拾遗--多线程:临界区解决子线程的互斥

                  C++拾遗--多线程:临界区解决子线程的互斥

前言

    为了解决子线程的互斥问题,windows系统提出了关键段或临界区(CRITICAL_SECTION)的概念。它一共有四个共两对操作:初始化、销毁,进入、离开。它们定义在头文件synchapi.h中。

1.初始化变量

VOID WINAPI InitializeCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
    );

2.销毁变量

VOID WINAPI DeleteCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
    );

3.进入临界区域

VOID WINAPI EnterCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
    );

函数说明:系统保证各个子线程互斥的进入临界区域

4.离开临界区域

VOID WINAPI LeaveCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
    );

四个函数的使用都相当简单,传入CRITICAL_SECTION类型的变量地址即可。


正文

程序示例

下面我们使用关键段来解决子线程的互斥问题,程序代码类似于原子操作解决线程冲突。每一个子线程都对同一个全局变量累加10。这次我们开启50个子线程,查看最后的累加结果。

#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <Windows.h>
#define Thread_NUM 50
CRITICAL_SECTION cs;
int g_count = 0;
void count(void *p)
{
	Sleep(100);    //do some work  
	//每个线程把g_count加1共10次  
	for (int i = 0; i < 10; i++)
	{
		//进入临界区域
		EnterCriticalSection(&cs);
		g_count++;
		//离开临界区域
		LeaveCriticalSection(&cs);
	}
	Sleep(100);   //do some work  
}
int main(void)
{
	printf("******临界区解决子线程冲突演示***by David***\n");
	//初始化关键段变量cs
	InitializeCriticalSection(&cs);
	//共创建Thread_NUM个线程  
	HANDLE handles[Thread_NUM];
	//共验证10次
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < Thread_NUM; j++)
		{
			handles[j] = _beginthread(count, 0, NULL);
		}
		WaitForMultipleObjects(Thread_NUM, handles, 1, INFINITE);
		printf("%d time g_count = %d\n", i, g_count);
		//重置  
		g_count = 0;
	}
	//销毁关键段变量cs
	DeleteCriticalSection(&cs);
	getchar();
	return 0;
}
运行

技术分享

从运行结果看,使用关键段确实可以解决子线程的冲突问题。在g_count++;的前后我们加上关键段的进入和离开,使这句代码成为了“临界”区域。至此,g_count++;的操作就十分类似于原子操作。下面我们来详细分析下关键段的使用原理。


关键段原理

关键段的定义

在minwinbase.h中

typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;

而在winnt.h中

typedef struct _RTL_CRITICAL_SECTION {
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
    //
    //  The following three fields control entering and exiting the critical
    //  section for the resource
    //
    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;        // from the thread‘s ClientId->UniqueThread
    HANDLE LockSemaphore;
    ULONG_PTR SpinCount;        // force size on 64-bit systems when packed
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;


关键段就是一结构体。具体原因后续分析……





本专栏目录

所有内容的目录

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。