C++多线程同步之Semaphore(信号量)
一、线程间同步的几种方式
从上篇博文中可以发现,当多个线程对同一资源进行使用时,会产生“争夺”的情况,为了避免这种情况的产生,也就出现了线程间的同步这个技术。线程间的同步有多种方式,在接下来的博文中我会依次介绍几种主流的同步方式,以及他们之间的区别。在本篇博文中将介绍使用信号量Semaphore达到线程间同步的目的。老规矩,所有代码都讲在win32平台和Linux平台下都实现一遍。
相关函数和头文件
//头文件
#include <windows.h>
//创建信号量API
HANDLE WINAPI CreateSemaphore(
_In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,//指向SECURITY_ATTRIBUTES的指针;
_In_ LONG lInitialCount, //信号量对象的初始值;
_In_ LONG lMaximumCount, //信号量对象的最大值,这个值必须大于0;
_In_opt_ LPCTSTR lpName //信号量对象的名称;
);
//等待信号量API
DWORD WINAPI WaitForSingleObject(
_In_ HANDLE hHandle, //信号量对象句柄
_In_ DWORD dwMilliseconds //等待信号量时间,INFINET代表永久等待;
);
返回值:
WAIT_ABANDONED(0x00000080L) 表示拥有信号量的线程再终止前未释放该信号量;
WAIT_OBJECT_0(0x00000000L) 表示等到了信号量;
WAIT_TIMEOUT(0x00000102L) 表示等待超时;
WAIT_FAILED((DWORD)0xFFFFFFFF) 表示该函数执行失败,用GetLastError()得到错误码;
//释放信号量句柄
BOOL WINAPI ReleaseSemaphore(
_In_ HANDLE hSemaphore, //信号量对象句柄;
_In_ LONG lReleaseCount, //信号量释放的值,必须大于0;
_Out_opt_ LPLONG lpPreviousCount //前一次信号量值的指针,不需要可置为空;
);
返回值:成功返回非0;
Win32平台下源码
#include <iostream>
#include <windows.h>
using namespace std;
HANDLE g_hSemaphore = NULL; //声明信号量变量
unsigned long WINAPI Fun(LPVOID lpParamter)
{
int iRunTime = 0;
//执行100次跳出
while(++iRunTime<100)
{
WaitForSingleObject(g_hSemaphore, INFINITE); //信号量值-1
cout << "Fun() is running!"<<endl;
ReleaseSemaphore(g_hSemaphore, 1, NULL); //信号量值+1
Sleep(10);
}
ExitThread(-1);
}
int main()
{
//创建信号量对象
g_hSemaphore = CreateSemaphore(NULL //信号量的安全特性
, 1 //设置信号量的初始计数。可设置零到最大值之间的一个值
, 1 //设置信号量的最大计数
, NULL //指定信号量对象的名称
);
if(NULL == g_hSemaphore)
{
cout << "create hSemaphore failed! error_code:"<<GetLastError()<<endl;
return 0;
}
int iRunTime = 0;
unsigned long ulThreadId = 0;
//创建一个子线程
HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, &ulThreadId);
//执行100次跳出
while(++iRunTime<100)
{
WaitForSingleObject(g_hSemaphore, INFINITE); //信号量值-1
cout << "main() is running, Thread id is " << ulThreadId <<endl;
ReleaseSemaphore(g_hSemaphore, 1, NULL); //信号量值+1
Sleep(10);
}
system("pause");
return 0;
}
执行结果:
可见未对屏幕资源产生“争夺”的情况,达到线程同步的目的。
Linux平台
相关函数和头文件
int sem_init(sem_t *sem, int pshared, unsigned int value);
1)pshared==0 用于同一多线程的同步;
2)若pshared>0 用于多个相关进程间的同步(即由fork产生的);
int sem_getvalue(sem_t *sem, int *sval);
取回信号量sem的当前值,把该值保存到sval中。
若有1个或更多的线程或进程调用sem_wait阻塞在该信号量上,该函数返回两种值:
1) 返回0
2) 返回阻塞在该信号量上的进程或线程数目
linux采用返回的第一种策略。
sem_wait(或sem_trywait)相当于P操作,即申请资源。
int sem_wait(sem_t *sem); // 这是一个阻塞的函数
测试所指定信号量的值,它的操作是原子的。
若sem>0,那么它减1并立即返回。
若sem==0,则睡眠直到sem>0,此时立即减1,然后返回;
int sem_trywait(sem_t *sem); // 非阻塞的函数
其他的行为和sem_wait一样,除了:
若sem==0,不是睡眠,而是返回一个错误EAGAIN。
sem_post相当于V操作,释放资源。
int sem_post(sem_t *sem);
把指定的信号量sem的值加1;
呼醒正在等待该信号量的任意线程。
源码
#include <iostream>
#include <pthread.h>
#include <semaphore.h>
using namespace std;
static sem_t g_semaphore;
static const int g_iRunTime = 5000;
void* Fun(void* ptr)
{
int iRunTime = 0;
while(++iRunTime< g_iRunTime)
{
sem_wait(&g_semaphore);
cout<< "Fun() is running!" << endl;
sem_post(&g_semaphore);
usleep(100);
}
}
int main()
{
pthread_t hHandle;
sem_init(&g_semaphore, 0, 1);
int iRet = pthread_create(&hHandle, NULL, Fun, NULL); //create a thread;
if(0 != iRet)
{
cout << "Create thread failed!" << endl;
}
sleep(1);
int iRunTime = 0;
while(++iRunTime<g_iRunTime)
{
sem_wait(&g_semaphore);
cout << "main is running!" << endl;
sem_post(&g_semaphore);
usleep(100);
}
pthread_join(hHandle, NULL);
return 0;
}
执行结果
达到同步效果!
关于Linux信号量
Linux信号量比Windows要复杂,上述例子只是使用了其中最常用的一种,还有许多其他种类的信号量,后期会补上一篇关于Linux信号量详解的内容。
相关文章
- 汽车收费 C++ PTA
- 基于c++11新标准开发一个支持多线程高并发的网络库
- C++ vector 和 map的删除
- c++ (QT)笔记
- C++多线程编程(入门实例)
- C++ 冒泡排序
- c++模板的简单认识
- 多线程 用户级线程和内核级线程 from C++多核高级编程
- 《C++多线程编程实战》——1.3 程序结构、执行流和运行时对象
- 《C++多线程编程实战》——2.3 解释进程模型
- 《C++多线程编程实战》——2.5 进程间通信(IPC)
- 《C++多线程编程实战》——2.6 解决典型的IPC问题
- 《Visual C++ 2012 开发权威指南》——2.3 Visual C++2012的语言新特性(3)
- 《C++面向对象高效编程(第2版)》——2.14 类的实现者
- 《C和C++代码精粹》——1.5 类型安全连接
- 《数字图像处理与机器视觉——Visual C++与Matlab实现》——1.3 Matlab的图像转换
- C++Primer第五版学习(函数部分 四)函数重载
- 基于QT(C++) 实现哈夫曼压缩(多线程)【100010471】
- Swift 基础100问之 02 在Swift中如何实现C/C++中的#ifdef功能 编译器预处理
- C运行时库(C Run-time Library)详解(提供的另一个最重要的功能是为应用程序添加启动函数。Visual C++对控制台程序默认使用单线程的静态链接库,而MFC中的CFile类已暗藏了多线程)
- tensorflow1.x——如何在C++多线程中调用同一个session会话
- kafka的c/c++高性能客户端librdkafka简介
- Windows下C++多线程编程(入门实例)
- C/C++教程 第二章 —— 快速入门C/C++
- C++实操 - 如何在程序中delay一段时间