C ++事件上的信号量

今天,我将简要讨论如何基于同步对象“事件”实现信号量。

首先检查一下定义。

1.什么是同步,为什么需要同步?


显然,我们可以通过几种方式执行一系列操作。 最简单-顺序和并行。 某些操作的并行执行可以通过运行各种线程来实现。 这个想法很简单:向每个线程分配一些基本(或不是基本)操作,并以一定顺序运行它们。 一般来说,我们可以同时启动所有这些程序-当然,我们会及时获得收益。 这是可以理解的:一个输出一个接一个的单词是一件事,而同时输出例如100个单词的另一件事是。 100倍的时间增益(正负,不包括延迟等)。 但是原始任务可能涉及严格的操作序列。

例如:

  • 开启档案
  • 将文字写入档案
  • 关闭档案

温室示例是专门采取的(很明显,这里不需要并行处理,所有操作都可以按顺序完成),但是作为培训任务,它将完全起作用,最重要的是,在示例中可以清楚地看到一致执行的需求。 或者这是另一个示例,略有不同:

  • 生成三个随机数序列
  • 顺序显示

在这里,可以通过三个不同的线程同时执行第一个点,但是最后一个结论必须按顺序进行,并且只有在计算出第一点之后才能完成。

通常,并行性任务可能非常不同,并且需要一些工具来同步线程。

2.线程同步工具


Windows.h实现了很多常规同步工具(所谓的“同步对象”)。 其中主要的是:关键区域,事件,互斥体,信号量。 是的,对于信号量,windows.h中已经有一个实现。 “那么为什么要编程呢?”你问。 首先,要更好地了解它的工作原理。 其次,C ++的额外实践尚未阻止任何人:)

鉴于我们将使用事件,我们将解释它是什么以及如何使用它。

事件用于通知挂起的线程。 也就是说,实际上,这是流的信号-它可以被触发或尚未被触发。 从这个对象的意义上讲,它具有某种信号状态并具有调整它的能力(复位/“打开”)。

因此,在连接windows.h之后,我们可以使用以下命令创建事件:

HANDLE CreateEvent ( LPSECURITY_ATTRIBUTES lpEventAttributes, //   BOOL bManualReset, //  : TRUE -  BOOL bInitialState, //  : TRUE -  LPCTSTR lpName //   ); 

如果函数成功,将返回事件句柄。 如果无法创建对象,则将返回NULL。

要将事件的状态更改为信号,我们使用以下函数:

 BOOL SetEvent ( HANDLE hEvent //   ); 

如果成功,将返回一个非零值。

现在关于信号量。 信号量旨在控制同时运行的线程数。 假设我们有1000个线程,但一次只能工作2个,这是在信号量的帮助下进行的调整类型。 以及实现了哪些功能来与此同步对象一起使用?

要创建类似于事件的信号灯:

 HANDLE CreateSemaphore ( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, //   LONG lInitialCount, //     LONG lMaximumCount, //    LPCTSTR lpName //   ); 

如果成功,则获得指向信号量的指针,如果失败,则获得NULL。

信号量计数器不断变化(线程正在运行,并且出现空位),因此需要定期更改信号量的状态。 使用以下功能可以完成此操作:

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

如果成功,则返回值不为零。

另外值得关注的功能:

 DWORD WaitForSingleObject( HANDLE hHandle, //   ,     DWORD dwMilliseconds //     ); 

在返回的值中,我们特别关注2:WAIT_OBJECT_0-表示对象的状态为信号; WAIT_TIMEOUT-我们没有在分配的时间内等待来自对象的信号状态。

3.任务本身


总的来说,我们的任务是在常规函数上编写类似物。 我们不会使任务复杂化很多,我们将进行“近似实现”。 最主要的是保留标准信号量的定量特征。 带有注释的代码可以在GitHub找到

由于任务本身很简单,因此我们不会使本文特别复杂,但是对于某些人来说可能会派上用场:)

Source: https://habr.com/ru/post/zh-CN476940/


All Articles