VC++ 线程同步之信号量(Semaphore)

VC++ 线程同步之信号量(Semaphore)

信号量与互斥量类似,唯一的区别是多个对象可持有信号量的所有权。假设有一个复杂的数学运算要用到整个逻辑

CPU 核。如果每个核只运行一个线程,计算结果没问题,但是,如果每个核运行多个线程,计算结果就有问题

了。另外,还假设计算过程中所需的线程数量比同时工作的逻辑核的数量多。

最好选择信号量对象来处理这种情况,我们可以把信号量对象的最大值设置为机器的逻辑核数量。当线程数量不超

过核的数量时,它们能同时工作,优化计算过程。当线程数量超过逻辑核的数量时,一些线程将被挂起,等待其他

线程执行完毕。

每次只有一个线程可以获得互斥量,与此不同的是,只要未超过可持有信号量所有权的最大数量,信号量仍然处于

触发状态。如果一个线程要等待信号量,那么在其他线程释放信号量之前它将被挂起。

信号量涉及的函数:

创建或打开一个现有的信号量对象,可以使用CreateSemaphore:

HANDLE WINAPI CreateSemaphore(
    LPSECURITY_ATTRIBUTES  lpSemaphoreAttributes,
    LONG                   lInitialCount,
    LONG                   lMaximumCount,
    LPCTSTR                szName
);

第 1个参数是安全特征,可以指定也可以空出不填(如果不填,该参数将被设为默认值)。第 2个参数没置初始数目以确定信号量的初始触发状态。如果设置为 0,信号量为未触发状态。这个值必须小于第3个参数lMaxmumCount 的值。lMaxmumCount 表示可同时持有信号量所有权的最多对象(线程)数目。必须提供信号量名(szName)。

使用 OpenSemaphore 也可以打开一个现有的信号量:

HANDLE WINAPI OpenSemaphore(
    DWORD    dwDesiredAccess,
    BOOl     bInheritHandle,
    LPCTSTR  szName
);

如果信号量不存在,该例程将失败并返回 NULL。线程操作完毕后,必须释放信号量以递减计数器计数,这样其他

线程才能获得信号量。用 ReleaseSemaphore API 可以释放信号量:

BOOL WINAPI ReleaseSemaphore(
    HANDLE hSemaphore,
    LONG lReleaseCount,
    LPLONG lpPreviousCount
);

这个API有3个参数。第1个参数是之前 CreateSemaphore 或 OpenSemaphore 返回的信号量的句柄。第2个参数

是信号量要递减的对象数量,其值通常是 1,因为线程总是逐个被释放的。有一种情况例外线程 A 获得一个信号

量,然后要创建也需要信号量的线程 B。线程A 在线程B 创建好之前完成了自己的任务,线程B不知道它将被强制

终止,所以线程A要终止子线程且递减信号量为2。

代码示例

以下是使用 Win32API 信号量实现的线程同步代码的完整 C++ 实现。该示例使用两个线程,一个线程在计算 1 到 100 的总和,另一个线程在计算 101 到 200 的总和。在两个线程完成计算后,主线程将这两个部分的总和相加并打印结果。

#include <iostream>
#include <Windows.h>

using namespace std;

// 定义信号量和互斥量句柄
HANDLE sem1, sem2, mutex1, mutex2;

// 定义共享数据
int sum1, sum2;

// 计算 1 到 100 的总和
DWORD WINAPI Sum1(LPVOID lpParam) {
    int i;
    for (i = 1; i <= 100; i++) {
        WaitForSingleObject(mutex1, INFINITE);    // 获取互斥量
        sum1 += i;                              // 计算部分总和
        ReleaseSemaphore(sem1, 1, NULL);            // 发送信号量
        ReleaseMutex(mutex1);                    // 释放互斥量
    }
    return 0;
}

// 计算 101 到 200 的总和
DWORD WINAPI Sum2(LPVOID lpParam) {
    int i;
    for (i = 101; i <= 200; i++) {
        WaitForSingleObject(mutex2, INFINITE);
        sum2 += i;
        ReleaseSemaphore(sem2, 1, NULL);
        ReleaseMutex(mutex2);
    }
    return 0;
}

int main() {
    DWORD threadID1, threadID2;

    sem1 = CreateSemaphore(NULL, 0, 1, NULL);
    sem2 = CreateSemaphore(NULL, 0, 1, NULL);
    mutex1 = CreateMutex(NULL, false, NULL);
    mutex2 = CreateMutex(NULL, false, NULL);

    // 创建两个线程
    HANDLE hThread1 = CreateThread(NULL, 0, Sum1, NULL, 0, &threadID1);
    HANDLE hThread2 = CreateThread(NULL, 0, Sum2, NULL, 0, &threadID2);

    // 等待信号量
    WaitForSingleObject(sem1, INFINITE);
    WaitForSingleObject(sem2, INFINITE);

    // 计算总和
    int sum = sum1 + sum2;
    cout << "Sum = " << sum << endl;

    // 释放所有句柄
    CloseHandle(hThread1);
    CloseHandle(hThread2);
    CloseHandle(sem1);
    CloseHandle(sem2);
    CloseHandle(mutex1);
    CloseHandle(mutex2);

    return 0;
}

代码实现中,分别创建了线程运行 Sum1Sum2 函数,这两个函数分别计算 1 到 100 和 101 到 200 的部分总和。在每个线程的迭代中,获取互斥量,计算部分总和,释放信号量和释放互斥量。最后,主线程等待两个线程每个都释放了它的信号量时,将两个部分总和相加,并打印结果。 最后,释放所有句柄并返回 0。

该文章会更新,欢迎大家批评指正。

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,
分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:
服务器课程:C++服务器

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值