多线程同步-关键段(临界区)

1.简介

关键段是一小段代码,它在执行之前需要独占对一些共享资源的访问权。这种方式可以让多行代码以“原子方式”来对资源进行操控。

使用的函数:

等待指定临界区对象的所有权。当调用线程被授予所有权时,函数返回。

void EnterCriticalSection(
  [in, out] LPCRITICAL_SECTION lpCriticalSection
);

 释放指定临界区对象的所有权。

void LeaveCriticalSection(
  [in, out] LPCRITICAL_SECTION lpCriticalSection
);

初始化临界区对象。在调用EnterCriticalSection之前必须调用初始化函数。

void InitializeCriticalSection(
  [out] LPCRITICAL_SECTION lpCriticalSection
);

释放资源。 

void DeleteCriticalSection(
  [in, out] LPCRITICAL_SECTION lpCriticalSection
);

2.示例

此示例,创建了两个线程。对全局变量g_sum进行累加操作。

// Interlocked.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

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

DWORD WINAPI add0(LPVOID);
DWORD WINAPI add1(LPVOID);

int g_count = 10;
long g_sum = 0;
CRITICAL_SECTION g_cs;

int main()
{
    HANDLE aThread[2];
    DWORD ThreadID;

    InitializeCriticalSection(&g_cs);

    aThread[0] = CreateThread(
        NULL,       // default security attributes
        0,          // default stack size
        (LPTHREAD_START_ROUTINE)add0,
        NULL,       // no thread function arguments
        0,          // default creation flags
        &ThreadID); // receive thread identifier

    if (aThread[0] == NULL)
    {
        printf("CreateThread error: %d\n", GetLastError());
        return 1;
    }


    aThread[1] = CreateThread(
        NULL,       // default security attributes
        0,          // default stack size
        (LPTHREAD_START_ROUTINE)add1,
        NULL,       // no thread function arguments
        0,          // default creation flags
        &ThreadID); // receive thread identifier

    if (aThread[1] == NULL)
    {
        printf("CreateThread error: %d\n", GetLastError());
        return 1;
    }


    WaitForMultipleObjects(2, aThread, TRUE, INFINITE);

    // Close thread and mutex handles

    for (int i = 0; i < 2; i++)
        CloseHandle(aThread[i]);

    DeleteCriticalSection(&g_cs);

    return 0;

}

DWORD WINAPI add0(LPVOID lpParam)
{
    UNREFERENCED_PARAMETER(lpParam);

    EnterCriticalSection(&g_cs);
    g_sum = 0;

    for (int i = 0; i < g_count; i++)
    {
        g_sum += i;
    }

    printf("Thread %d g_sum = %d\n",
        GetCurrentThreadId(), g_sum);
   
    LeaveCriticalSection(&g_cs);

    return g_sum;
}

DWORD WINAPI add1(LPVOID lpParam)
{
    UNREFERENCED_PARAMETER(lpParam);

    EnterCriticalSection(&g_cs);
    g_sum = 0;

    for (int i = 0; i < g_count; i++)
    {
        g_sum += i;
    }

    printf("Thread %d g_sum = %d\n",
        GetCurrentThreadId(), g_sum);

    LeaveCriticalSection(&g_cs);

    return g_sum;
}

首先需要定义一个CRITICAL_SECTION g_cs数据结构。然后把需要访问共资源(g_sum)的代码放在EnterCriticalSection和LeaveCriticalSection之间,注意传入的g_cs是地址。

当不能用Interlocked函数解决同步问题的时候,应该试一试关键段。它的最大好处在于它们非常容易使用,而且它们内部也使用了Interlocked函数,最大缺点在于它们无法用来在多个进程之前对线程进行同步。

3.互斥量内核对象

多线程同步-互斥量内核对象_Mr.codeee的博客-CSDN博客互斥量内核对象用来确保一个线程独占对一个资源的访问。一个使用数量。一个线程ID(用于标识系统中的哪个线程当前拥有互斥对象)。一个递归计数器(用于指明该线程拥有互斥对象的次数)。如果线程ID是0(这是个无效ID),互斥对象不被任何线程所拥有,它处于触发状态。如果ID是个非0数字,那么一个线程就拥有互斥对象,它处于未触发状态。与所有其他内核对象不同, 操作系统对互斥量进行了特殊处理,允许它们违反一些常规的规则。创建互斥量。);psa:指向SECURITY_ATTRIBUTES结构的指针。https://blog.csdn.net/wzz953200463/article/details/127309051?spm=1001.2014.3001.5501

4.互斥量对象和关键段的异同

互斥量对象的行为特性与关键代码段相同,但是互斥对象属于内核对象,而关键代码段则属于用户方式对象。这意味着互斥对象的运行速度比关键代码段要慢。但是这也意味着不同进程中的多个线程能够访问单个互斥量对象,并且这意味着线程在等待访问资源时可以设定一个超时值。

特性互斥量对象关键段
运行速度
是否能够跨进程边界来使用
声明HANDLE hmtx;CRITICAL_SECTION cs;
初始化hmtx = CreateMutex(NULL,FALSE,NULL);InitializeCriticalSection(&es);
清除CloseHandle(hmtx);DeleteCriticalSection(&cs);
无限等待WaitForSingleObject(hmtx ,INFINITE);EnterCriticalSection(&cs);
0等待WaitForSingleObject(hmtx , 0);EnterCriticalSection(&cs);
任意等待WaitForSingleObject(hmtx,dwMilliseconds);不能
释放ReleaseMutex(hmtx);LeaveCriticalSection(&cs);
是否能够等待其他内核对象是(使用WaitForMultipleObjects或类似的函数)    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.codeee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值