目录

  • windows使用CreateEvent、SetEvent、ResetEvent、WaitForSingleObject
  • linux使用sem_init、sem_wait、sem_trywait、sem_post、sem_destroy

windows使用CreateEvent、SetEvent、ResetEvent、WaitForSingleObject

CreateEvent
功能:创建或打开一个命名的或无名的事件对象。

/*
参数1,lpEventAttributes,确定返回的句柄是否可被子进程继承,是NULL,此句柄不能被继承。
参数2,bManualReset,复位方式,true(手动,WaitForSingleObject后必须手动调用ResetEvent清除信号)false(自动,WaitForSingleObject后,系统自动清除事件信号);
参数3,bInitialState,初始状态,false为无信号,true为有信号;
参数4,lpName,信号名称,可以为Null;
返回值,返回事件句柄HANDLE。
*/
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset,BOOL bInitialState,LPCTSTR lpName);

1、所有线程都可以在一个等待函数中指定事件对象句柄。当指定的对象的状态被置为有信号状态时,单对象等待函数将返回。
2、对于多对象等待函数,可以指定为任意或所有指定的对象被置为有信号状态。当等待函数返回时,等待线程将被释放去继续运行。
3、使用SetEvent函数将事件对象的状态置为有信号状态。使用ResetEvent函数将事件对象的状态置为无信号状态。
4、当一个手动复原的事件对象的状态被置为有信号状态时,该对象状态将一直保持有信号状态,直至明确调用ResetEvent函数将其置为无符号状态。
5、当事件的对象被置为有信号状态时,任意数量的等待中线程,以及随后开始等待的线程均会被释放。
6、当一个自动复原的事件对象的状态被置为有信号状态时,该对象状态将一直保持有信号状态,直至一个等待线程被释放;系统将自动将此函数置为无符号状态。如果没有等待线程正在等待,事件对象的状态将保持有信号状态。
7、多个进程可持有同一个事件对象的多个句柄,可以通过使用此对象来实现进程间的同步。

SetEvent

//参数1,hEvent,一个内核事件对象的句柄。
//返回值,成功返回非零值,失败返回为0,调用GetLastError得到错误的详细信息。
BOOL SetEvent(HANDLE hEvent);

设置事件的状态为有状态,释放任意等待线程。
1、如果事件是手动的,此事件将保持有状态直到调用ResetEvent,这种情况下将释放多个线程;
2、如果事件是自动的,此事件设置为有状态,直到一个线程调用WaitForSingleObject,系统将设置事件的状态为无状态;
3、如果没有线程在等待,则此事件将保持有标记,直到一个线程被释放。

ResetEvent
把指定的事件对象设置为无信号状态。

//参数1,hEvent,一个内核事件对象的句柄。
//返回值,成功返回非0值,失败返回0值,调用GetLastError得到错误的详细信息。
BOOL ResetEvent(HANDLE hEvent);

1、一个事件对象一直都保持在无信号状态,直到显式调用 SetEvent or PulseEvent 函数把它设置到有信号状态。 这些无信号的事件对象会阻塞任何在内部调用wait函数的线程。
2、ResetEvent用于手动重置的事件对象。手动重置的对象在线程释放后必须手动置为无信号状态。 自动重置的事件对象在一个等待它成功的线程释放后会自动变为无信号状态。
3、重置一个无信号的事件对象没有任何效果。

WaitForSingleObject
用来检测hHandle事件的信号状态,在某一线程中调用该函数时,线程暂时挂起,如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果时间已经到达dwMilliseconds毫秒,但hHandle所指向的对象还没有变成有信号状态,函数照样返回。

/*
参数1,一个内核对象的句柄,可以是Event,Mutex,Semaphore(信号量),Process,Thread。
参数2,阻塞超时时长,单位毫秒1)传0:表示不阻塞,立即返回,返回值为WAIT_OBJECT_0。2)传>0:阻塞时长,超时时返回WAIT_TIMEOUT。3)传INFINITE:表示一直阻塞,直到等待句柄的状态发生改变。
返回值:
WAIT_ABANDONED :当hHandle为mutex时,如果拥有mutex的线程在结束时没有释放核心对象会引发此返回值。
WAIT_OBJECT_0 :指定的对象处于有信号状态,
WAIT_TIMEOUT :等待超时。
WAIT_FAILED :出现错误,可通过GetLastError得到错误代码。
*/
DWORD WaitForSingleObject(_In_ HANDLE hHandle, _In_ DWORD dwMilliseconds);

WaitForMultipleObjects

/*
参数1,nCount对象句柄的最大数量为MAXIMUM_WAIT_OBJECTS。此参数不能为零。
参数2,lpHandles指向的数组中的对象句柄数。一组对象句柄。该数组可以包含不同类型对象的句柄。它可能不包含同一句柄的多个副本。
参数3,bWaitAll,如果此参数为TRUE,则在lpHandles数组中的所有对象的状态发出信号时,该函数返回。如果为FALSE,则当任何一个对象的状态设置为信号时,该函数返回。在后一种情况下,返回值表示其状态导致函数返回的对象。
参数4,dwMilliseconds,超时间隔,以毫秒为单位。如果指定了非0值,则该函数将一直等到指定的对象发出信号或经过间隔。如果dwMilliseconds为0,则如果未发出指示对象,则该函数不会进入等待状态,它总是立即返回。如果dwMilliseconds是INFINITE,则仅在发出指定对象信号时才返回该函数。
返回值:
如果函数成功,返回值表示该事件导致该函数返回。这个值可以是下列之一。
WAIT_OBJECT_0到(WAIT_OBJECT_0 + nCount - 1如果bWaitAll为TRUE),则返回值表明所有指定对象的状态信号。
如果bWaitAll为FALSE,则返回值减去不是WAIT_OBJECT_0表示lpHandles数组的对象的满意指数的等待。如果多个对象在通话过程中信号成为,这是与所有的信号对象的最小索引值的信号对象的数组索引。
WAIT_ABANDONED_0至(WAIT_ABANDONED_0 + nCount - 1)如果bWaitAll为TRUE,则返回值表明所有指定对象的状态是触发的,并且至少对象之一,是一个废弃的互斥对象。如果bWaitAll为FALSE,则返回值减去WAIT_ABANDONED_0 表示一个废弃的互斥对象在lpHandles数组中的下标,满足等待。
WAIT_TIMEOUTThe超时间隔已过,由bWaitAll参数指定的条件得不到满足。
*/
DWORD WaitForMultipleObjects(DWORD nCount,const HANDLE* lpHandles,BOOL bWaitAll,DWORD dwMilliseconds);

测试demo
事件设置自动,初始无状态,主线程界面点击按钮触发设置事件为有状态,子线程调用WaitForSingleObject检查状态变化,按钮点击一次执行一次循环。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <windows.h>
void ThreadFunc(LPVOID pParam)
{MainWindow *mw = (MainWindow*)pParam;static int countflag = 0;while(mw->isloop){DWORD waitResult = WaitForSingleObject(mw->m_event, INFINITE);//一直不超时,直到事件变为有状态switch (waitResult) {case WAIT_OBJECT_0:printf("WaitForSingleObject,_hShutdownCaptureEvent\n");break;case WAIT_TIMEOUT:printf("WaitForSingleObject,timeout notification\n");break;default:printf("WaitForSingleObject,unexpected error\n");break;}printf("countflag=%d\n",countflag);countflag ++;}
}MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow)
{ui->setupUi(this);m_event = ::CreateEvent(NULL, FALSE, FALSE, NULL);//自动,初始无状态isloop = 1;hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFunc, this, 0, &m_ThreadID);
}MainWindow::~MainWindow()
{isloop = 0;CloseHandle(hThread);CloseHandle(m_event);delete ui;
}//界面按钮,点击设置为有状态
void MainWindow::on_pushButton_SetEvent_clicked()
{SetEvent(m_event);
}

点击三次按钮,打印:

WaitForSingleObject,_hShutdownCaptureEvent
countflag=0
WaitForSingleObject,_hShutdownCaptureEvent
countflag=1
WaitForSingleObject,_hShutdownCaptureEvent
countflag=2

linux使用sem_init、sem_wait、sem_trywait、sem_post、sem_destroy

sem_init
是Posix信号量操作中的函数,初始化一个定位在 sem 的匿名信号量。

//参数1,sem为指向信号量结构的一个指针
//参数2,pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;
//参数3,value给出了信号量的初始值。
//返回值,成功返回0,失败返回 -1,设置errno。
int sem_init (sem_t *sem, int pshared, unsigned int value);

sem_wait
用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减1
1、sem_wait函数是一个原子操作,它的作用是从信号量的值减去一个“1”
2、它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果对一个值为2的信号量调用sem_wait(),线程将会继续执行,信号量的值将减到1。
3、如果对一个值为0的信号量调用sem_wait(),这个函数就 会地等待直到有其它线程增加了这个值使它不再是0为止。
4、如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加 一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。

//参数,由sem_init调用初始化的信号量对象的指针
//返回值,成功返回0,失败时信号量的值不改动,返回-1.errno标识错误.
int sem_wait(sem_t * sem);

sem_trywait
sem_trywait是sem_wait的非阻塞版本。函数 sem_trywait和sem_wait有一点不同,即如果信号量的当前值为0,则返回错误而不是阻塞调用。错误值errno设置为EAGAIN。

int sem_trywait(sem_t *sem);

sem_post
给信号量的值加上一个“1”
1、它是一个“原子操作”---即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;
2、当有线程阻塞在这个信号量上时,调用这个函数会使其中一个线程不在阻塞,选择机制是由线程的调度策略决定的。

//返回值,成功返回 0;错误时,信号量的值没有更改,返回-1,并设置 errno 来指明错误。
int sem_post(sem_t *sem);

测试demo

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <pthread.h>
#include <semaphore.h>
void *thread_function(void *arg)
{MainWindow *mw = (MainWindow*)arg;static int countflag = 0;while (mw->isloop){sem_wait(&mw->bin_sem);//信号量为0时阻塞,大于0时往下运行printf("run,countflag=%d\n",countflag);countflag++;}return NULL;
}MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);int res = sem_init(&bin_sem, 0, 0);//初始化信号量if (res != 0){perror("Semaphore initialization failed");}isloop = 1;res = pthread_create(&a_thread, NULL, thread_function, this);//子线程调用sem_waitif (res != 0){perror("Thread creation failure");}
}
MainWindow::~MainWindow()
{isloop = 0;sem_post(&bin_sem);//再执行一次子线程循环,否则sem_wait阻塞无法退出sem_destroy(&bin_sem);pthread_join(a_thread,NULL);delete ui;
}//点击按钮,调用sem_post使信号量+1
void MainWindow::on_pushButton_sem_post_clicked()
{sem_post(&bin_sem);
}

点击三次按钮,关闭程序,打印

run,countflag=0
run,countflag=1
run,countflag=2
run,countflag=3

C++信号量实现线程间同步,windows使用SetEvent,linux使用sem_t,QT测试相关推荐

  1. Linux多线程编程---线程间同步(互斥锁、条件变量、信号量和读写锁)

    本篇博文转自http://zhangxiaoya.github.io/2015/05/15/multi-thread-of-c-program-language-on-linux/ Linux下提供了 ...

  2. LinuxC高级编程——线程间同步

    LinuxC高级编程--线程间同步 宗旨:技术的学习是有限的,分享的精神是无限的. 1. 互斥锁mutex 多个线程同时访问共享数据时可能会冲突.对于多线程的程序,访问冲突的问题是很普遍的,解决的办法 ...

  3. IOT-OS之RT-Thread(六)--- 线程间同步与线程间通信

    文章目录 一.IPC对象管理 1.1 IPC对象控制块 1.2 IPC对象接口函数 二.线程间同步对象管理 2.1 信号量对象管理 2.2 互斥量对象管理 2.3 事件集对象管理 三.线程间通信对象管 ...

  4. 线程间同步和通信,event semaphore mailbox

    线程间同步和通信,event semaphore mailbox 1. 概述 2. 事件event 3. wait_order() 4. 旗语(semaphore) 5. semaphore::get ...

  5. 进程间通信 和 线程间同步

    以前经常搞混,所以记录下来. 进程间通信主要是指多个进程间的数据交互. 而线程间同步主要指维护多个线程之间数据准确.一致性. 一.进程间通信主要有以下几种方式: 管道(pipe):管道是一种半双工的通 ...

  6. 用户方式中线程的同步——Windows核心编程学习手札之八

    用户方式中线程的同步 --Windows核心编程学习手札之八 系统中所有线程都必须拥有对各种系统资源的访问权,这些资源包括内存堆栈.串口.文件.窗口和许多其他资源.如果一个线程需要独占对资源的访问权, ...

  7. 线程间同步的几种方法--互斥锁,条件变量,信号量,读写锁

    一.互斥锁(mutex) 锁机制是同一时刻只允许一个线程执行一个关键部分的代码. 1 . 初始化锁 int pthread_mutex_init(pthread_mutex_t *mutex,cons ...

  8. 铂金04:令行禁止-为何说信号量是线程间的同步利器

    欢迎来到<并发王者课>,本文是该系列文章中的第17篇. 在并发编程中,信号量是线程同步的重要工具.在本文中,我将带你认识信号量的概念.用法.种类以及Java中的信号量. 信号量(Semap ...

  9. 操作系统:进程间通信与线程间同步

    进程线程通信方式之间的差异 每个进程有自己的地址空间.两个进程中的地址即使值相同,实际指向的位置也不同.进程间通信一般通过操作系统的公共区进行.   同一进程中的线程因属同一地址空间,可直接通信. 不 ...

最新文章

  1. 5G NGC — AF 的 Service information
  2. Linux(三) 运行级别
  3. mysql 数据库基础教程(一)
  4. html炫酷动态时钟代码,js动态炫酷数字时钟
  5. Hive——多行转一行及一行转多行
  6. ips细胞再生视网膜研究进展
  7. 按头安利 好看又实用的SolidEdge 3d模型素材看这里
  8. Windows命令行常用命令
  9. 【电路】电容(四)——旁路电容
  10. APP性能测试——启动耗时测试
  11. Android项目实战系列—基于博学谷(四)我的模块(上)
  12. 1.ME32F103单片机ADC和DMA
  13. linux版docker安装镜像
  14. 谐振电路及品质因数(三)
  15. 超赞!60种数据可视化图表使用场景及制作工具整理大全 !
  16. 刚体的相对运动与机器人连杆的运动
  17. 美国哈佛大学图书馆凌晨4点座无虚席
  18. Fliqlo屏幕保护工具不能用了!!!
  19. Java游戏服务器开发之A星算法
  20. 好用的桌面日历软件desktopcal

热门文章

  1. 基于Redis的BloomFilter实现
  2. 算法:求10万以内的质数
  3. 【GlobalMapper精品教程】043:图片自动矢量化
  4. 【编程不良人】快速入门SpringBoot学习笔记06---RestFul、异常处理、CORS跨域、Jasypt加密
  5. 苹果xr黑屏转圈圈解决方法_苹果xr黑屏转圈打不开也关不掉怎么办
  6. 感触极深的一篇文章(迷失方向的你不妨看看)
  7. [附源码]SSM计算机毕业设计中青年健康管理监测系统JAVA
  8. MySQL 基础知识入门教程
  9. 微信小程序的消息推送的token的配置
  10. “温水煮青蛙”的实验论证