指南:工作者线程和信号量(续)

【Window】线程同步方式4——信号量 1 信号量 信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。 在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量 阅读详情

http://www.thebits.org/tutorials/mjsema1.asp

 

指南:工作者线程和信号量

©Malcolm Smith, 14th October 2002

 

创建工作者线程并等待它们结束

    

  那么在是何种境况下您可以创建使应用程序冻结直到所有的线程完成了它们的处理的线程呢?

 

    通常的情景是您的应用程序开始多线程,在转到下一个独立的任务前,需要等待所有的这些线程完成时。

    

    本文我将使用和前面一样的线程类,创建一些实例并等待它们结束。

 

    下面我进行深入讨论(一些代码将被省略):

    

for(int i = 0; i < 10; i++)
{
  Counter++;
  LabelIndex = (Counter-1) % 6;
  TWorkerThread *Thread = new TWorkerThread(true, LabelArray[LabelIndex], 50);
  Thread->FreeOnTerminate = true;
  Thread->Resume();
}

 

 

    上面同样的代码创建10个线程(现在请忽略它们都有同样标签的事实)

 

    我们无法得知所有的线程何时全部完成。这可以通过在另外的线程中创建这些独立的线程,使用一个知道有多少当前运行线程的ThreadCounter来实现。

 

    这就是上面的线程类被称为工作者线程的地方。下面我创建一个主人线程,通过该线程创建这些工作者线程,使用计数器去判断它们何时都结束了。

 

    注意,这里最初的代码将会导致前面提到的死锁。更正的版本稍后会涉及。

 

    这是类定义:

class THostThread : public TThread

{

private:

  int Counter;

  int MaxThreads;

  TLabel* LabelArray[6];

protected:

  void __fastcall Execute(void);

  void __fastcall HasCompleted(TObject *Sender);

public:

  __fastcall THostThread(bool CreateSuspended, TLabel* ALabelArray[6],

          int AMaxThreads);

};

 

 

此刻先别管 AmaxThreads参数。

 

该类将创建10个线程,并等待它们全部完成。实现如下:

 

__fastcall THostThread::THostThread(bool CreateSuspended, TLabel *ALabel,

        int AMaxThreads) : TThread(CreateSuspended), pLabel(ALabel)

{

Counter = 0;

MaxThreads = AMaxThreads;

memcpy(LabelArray, ALabelArray, sizeof(TLabel*) * 6);

}

 

 

void __fastcall THostThread::Execute(void)

{

int LabelIndex;

 

// 创建10个线程 ,增加我们的计数器

for(int i = 0; i < 10; i++)

  {

  Counter++;

 

  LabelIndex = (Counter-1) % 6;

 

  TWorkerThread *Thread = new TWorkerThread(true, LabelArray[LabelIndex], 50);

  Thread->FreeOnTerminate = true;

  Thread->OnTerminate = HasCompleted; // method used to decrement counter

  Thread->Resume();

  }

 

// 等待所有线程结束

while(!Terminated && Counter > 0)

  Sleep(1);

}

 

void __fastcall THostThread::HasCompleted(TObject *Sender)

{

Counter--;

}

 

  现在代码看起来很好,Execute方法创建10个线程,每个线程结束后让计数器减1。Execute方法直到所有的线程都结束才返回。

 

  完整起见,您可能需要这样实现这个线程的创建。

 

THostThread *Thread = new THostThread(true, LabelArray, 3);

Thread->FreeOnTerminate = true;

Thread->Resume();

Thread->WaitFor();

 

  猜猜会发生什么?您的应用程序在最后一行会产生难挨的中断。因为主VCL线程被挂起直至主人线程“Thread”返回。工作者线程仍然尝试通过同步去更新主VCL线程(此时是挂起的)的标签。当您根本不用同步但又使用OnTerminate 事件时类似的问题也会发生。这个事件被主VCL线程的上下文调用从而导致同样的问题。

 

  那么您如何克服这个问题呢?

 

  基本上,您需要设计您的GUI(或者业务对象),可以让它继续处理消息,但是它要知道在您所创建的线程告知它时才做相应的操作。

 

  下面我展示一下在一个GUI应用程序中使用TActionList如何这么做。

 

  演示程序中标名“Run Host Thread”的按纽是和ActionList组件关联的。当您双击这个动作列表组件时,您会看到HostThreadButton项。我使用它的唯一目的就是激活或禁用上面提到的按钮。在一个大的应用程序中您可能还有您的主人线程运行时需要禁用的菜单项或者其他控件。将这些控件都分配给这个动作,您可以通过一行代码去激活或禁用它们。(例如:)

 

   HostThreadButton->Enabled = false;

 

  上例中,所有连接到这个动作的控件都将被禁用。

 

  演示程序中的按纽自身被btnHostThread调用,它的Action属性指向HostThreadButton。这样当HostThreadButton被禁用,连接它的按钮同时被禁用。如果您看btnHostThread按钮的OnClick事件,您会看到没有指定事件处理。那么当按钮被按下时,代码是如何执行的呢?

我先前声明这个按钮的action属性制向HostThreadButton动作。您看这个动作,它有一个OnExecute事件,无论何时点击按钮,这个方法都会被调用。 如果我们有和这个动作关联的菜单项,那么菜单项被选上时同样的代码也会运行。

 

  这是这个动作的OnExecute事件的部分代码(没有注解)

 

THostThread *Thread = new THostThread(true, LabelArray, 3);

Thread->FreeOnTerminate = true;

Thread->OnTerminate = HostThreadHasCompleted;

Thread->Resume();

HostThreadButton->Enabled = false;

 

  这时,我们已经通知主人线程在它结束时去调用HostThreadHasCompleted方法。猜猜下面我们要做什么?

 

void __fastcall TForm1::HostThreadHasCompleted(TObject *Sender)

{

HostThreadButton->Enabled = true;

}

 

  这么做没有奖品。 如果您运行演示程序的这部分,您会看到6个标签被更新,按钮最初被禁用,当所有的线程完成后被重新激活。

 

  由于演示程序的简单,您看不到10个线程在运行,因为我们只有6个标签.

 

  下面我们继续进一步修改代码。让它变成这个样子:让10个线程最终都运行,但同时最多只有3个线程在运行。我们的按钮直到10个线程完全完成才能被激活。

   

  我们要添加一个信号量到我们的主人类。信号量是个同步对象,我们用它来限制创建的线程的数目。这个对象通过处理它自己的计数器来工作。任何时候,计数器的值大于0就允许执行线程,当计数器等于0时,线程会中止,直到一个工作者线程结束增加了信号量的计数器。

 

  请这么做。

 

  添加信号量句柄到THostThread类,在构造时创建这个信号量。

 

hSemaphore = CreateSemaphore(NULL, 3, 3, NULL);

 

  这创建了一个初始量为3、最大数目为3的信号量。接着,我们修改这个线程类的Execute方法,如下:(略去演示程序中的 try/__finally 块)

 

void __fastcall THostThread::Execute(void)

{

int LabelIndex = 0;

for(int i = 0; i < 10; i++)

  {

  Counter++;

 

  LabelIndex = LabelIndex % 6;

 

  ::WaitForSingleObject(hSemaphore, INFINITE);

 

  TWorkerThread *Thread = new TWorkerThread(true, LabelArray[LabelIndex], 50);

  Thread->FreeOnTerminate = true;

  Thread->OnTerminate = HasCompleted; // 用以减小计数器的方法

  Thread->Resume();

 

  LabelIndex++;

  }

 

while(!Terminated && Counter > 0)

  Sleep(1);

 

CloseHandle(hSemaphore);

}

 

  自然,每个线程结束时调用的方法也要做相应变动。

 

void __fastcall THostThread::HasCompleted(TObject *Sender)

{

Counter--;

::ReleaseSemaphore(hSemaphore, 1, NULL);

}

 

  这代码用以指引标签数组中下一项被修改,因为计数器的值从来不会大于2。

 

  调用WaitForSingleObject减少信号量处理的计数器。只要这个计数器大于0,线程就会继续执行。

 

  最初的循环创建了3个工作者线程,信号量计数器等于0,然后线程的执行停在WaitForSingleObject那里。

 

  任何一个工作者线程结束,HasCompleted方法被调用。ReleaseSemaphore调用再次增加信号量计数器从而允许主人线程继续执行。另一个工作者线程被创建。停止/继续/停止这样的过程一直进行直至最终所有10个工作者线程都被创建并完全执行。当最后的线程结束时,Execute方法完成,GUI的按钮再次被激活。

 

 运行演示程序,您会看到先是前面3个标签被更新,接着是后面3个,再接着是前面3个,最后是第4个(正好10个线程)。

 

 一旦您明白了,它就什么都不是了。

 

摘要

   

    这篇短文向您展示了如何创建简单的线程,并与主VCL线程同步事件。接着我们在一个主人线程中创建一些工作者线程,展示如何避免您的应用程序死锁(哦,至少使您知道了典型的原因)。

 

    最后,给出了一种使用信号量控制运行中的线程数的方法。

 

 

致礼

 

第1页 | 下载源代码

 

                                                                  (翻译:01soft)

线程同步——信号量和互斥锁的区别 文章目录两者在定义上的区别两者在语义上的区别信号量实现同步互斥锁实现同步条件变量实现同步条件变量满足之后唤醒其他线程加锁 两者在定义上的区别 信号量是用在多线程同步上的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程就可以进行某些动作,如果所有的线程都在(sem_wait),就会阻塞。 互斥锁是用在多线程的互斥的,对于临界资源的访问,需要互斥进行。也就是说当一个线程占用了一个资源的时... 阅读详情

相关推荐

c++ 多线程 信号量

信号量 Semaphore: 信号量常用3个函数,使用方便:

四夕日 月 3625

指南:工作者线程和信号量

http://www.thebits.org/tutorials/mjsema.asp 指南:工作者线程和信号量©Malcolm Smith, 14th October 2002     和我们的所有指南一样,这个指南没有义务去处理随时可能发生的任何事。请看我们的法律信息页面。本文基于一个在Windows XP Professional下测试过的项目。 目录

01Soft的专栏 1374

利用Civil3D装配设计器(SAC)建设高度形状自适应挡土墙

之前的模型都是点线,最后加一个形状,形成面。再根据现场测得的挡土墙的墙脚的点生成中心线(alignment),在中心线的基础上生成在现有地面(existing ground level)的高程(profile)。简单的说,该墙的墙趾啊,墙踵啊,基底下面那个把儿啊,都是随着高度变化的,而高度是随着下面这个高程图变化的。接着出现角度和Y的文本框,角度92,y 值随便写一个,然后重要的是,高程目标选路面(优先于Y值)。意思是P2的高度,也就是路面的高程,减去P10,墙脚高程,再减去墙脚埋进地里的深度0.8m。

测量老覃 的博客 1127

Linux多线程——使用信号量同步线程

信号量、同步这些名词在进程间通信时就已经说过,在这里它们的意思是相同的,只不过是同步的对象不同而已。但是下面介绍的信号量的接口是用于线程的信号量,注意不要跟用于进程间通信的信号量混淆。相似地,线程同步是控制线程执行和访问临界区域的方法。 一、什么是信号量 线程的信号量与进程间通信中使用的信号量的概念是一样,它是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作。如果一

ljianhui的专栏 6万+

python 写一个简单的 telegram bot (电报机器人)

replit.com 环境配置太恶心了,只能使用 poetry.poetry 这个名字是挺好听,用起来也是很垃圾,太恶心。heroku.com 注册登录不方便,需要各种验证。所以,部署的问题,目前搁置。

waterHBO的博客 3194

服务器冗余电源维修图纸,冗余热备份电源的电路图设计

在设计某高可靠性计算机系统时,要求其配套电源采取冗余设计。一般来说,可以采取的方案有容量冗余、冗余冷备份方式、并联均流的N+1备份方式、冗余热备份方式。容量冗余是指电源的最大负载能力大于实际负载,也就是“大马拉小车”,其缺点是不利于提高电源的效率,而且对提高电源的可靠性意义不大。冗余冷备份方式是指电源由两个或多个功能相同的单元模块组成,电源启动后由其中一个单元模块向设备供电,当工作单元发生故障时,...

weixin_39671509的博客 1995

信号量和线程池

但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误.死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所占有的不会释放的资源而处于永久等待的一种状态。1. 需要大量的线程来完成任务,且完成任务的时间比较短。POSIX信号量,用与同步操作,达到无冲突的访问共享资源目的,POSIX信号量可以用于线程间同步。等待信号量,会将信号量的值-1。

大学生一枚 1556

国产AI芯片实战:用寒武纪MLU370低成本部署DeepSeek-R1,实测推理效果与成本分析

本文详细介绍了寒武纪MLU370芯片在部署DeepSeek-R1大模型时的实战效果与成本优势。通过实测数据对比,MLU370在推理速度、能效比和本地化服务方面表现突出,尤其适合智能客服、内容生成等场景,可显著降低硬件投入和运维成本。

weixin_30613727的博客 456

Linux线程——线程互斥、同步、POSIX信号量

本文探讨了线程互斥与同步的概念及其实现方法。文章首先通过模拟抢票程序展示了多线程访问共享资源时可能出现的数据不一致问题,如票数出现负值的情况。分析指出,这是由于共享资源访问未加保护导致的线程安全问题。 为解决这一问题,文章介绍了使用pthread库中的互斥量(mutex)来保护共享资源。通过加锁机制,确保同一时间只有一个线程能访问关键代码段,从而避免了数据竞争。文章详细解析了ticket自减操作的汇编实现,结合操作系统分时调度的特点,解释了数据不一致的根本原因。 此外,文章还简要提及POSIX信号量的使用,

2301_79705195的博客 1447

aruco_ros实战:从USB摄像头到Marker位姿检测全流程解析

本文详细解析了使用aruco_ros从USB摄像头实现ArUco Marker检测与位姿估计的全流程。内容涵盖环境搭建、核心launch文件配置、可视化调试、位姿数据解析以及深度优化技巧,帮助开发者快速掌握ROS下的视觉定位关键技术,并集成到机器人项目中。

weixin_29230805的博客 265

AI 生成测试报告:从 2 小时到 10 分钟

本文分享了AI在汽车电子测试报告生成中的应用案例。作者以某车企ECU测试项目为例,对比传统报告方式(每天2小时)与AI辅助方式(每天20分钟),效率提升83%。文章详细介绍了测试报告的核心要素(测试概况、结果、问题分析、发布建议)和不同类型报告(日报、周报、总结报告)的特点,并提供了从CANoe导出测试数据的Python解析脚本示例。通过AI自动化处理,大幅减少了测试工程师在报告整理上的时间投入,为汽车电子测试工作提供了实用效率提升方案。

小明的博客 444

Hystrix 隔离策略:线程池、信号量

Hystrix内部,提供了两种模式隔离策略:信号量、线程池。(默认情况下,Hystrix使用线程池模式。) 信号量隔离,适应非网络请求,因为是同步的请求,无法支持超时,只能依靠协议本身 线程池隔离,即:每个实例都增加个线程池进行隔离 总结: 线程池隔离 信号量隔离 是否支持超时 支持,超时直接返回 不支持,如果阻塞,只能通过调 用协议(如:socket超时才能返回) 是否支持熔断 支持,当线程池到达maxSize后,再 请求会触发fallbac..

qq_35599414的博客 6460

化工过程设计软件:Aspen Plus二次开发_(2).二次开发概述

假设我们需要创建一个自定义的换热器模型,该模型能够计算两个物流之间的热交换量,并根据传热系数和换热面积进行调整。ACM 模型代码示例!自定义换热器模型!参数:传热系数 U (W/m^2·K)!换热面积 A (m^2)!热物流 T_hot_in (K), T_hot_out (K), F_hot (kmol/h)!冷物流 T_cold_in (K), T_cold_out (K), F_cold (kmol/h)!模型输入!U - 传热系数!A - 换热面积!

kkchenjj的博客 370

【Linux】信号量和线程池

主要讲解POSIX信号量的使用,通过信号量模拟实现基于环形队列的生产消费模型。引入线程池概念,并模拟实现

muyuteacher的博客 934

vue2 - 最新详细实现高德地图绘制动态热力图详细教程,在某区域或城市地图上做“热力图层“展示人流量、旅游人口密度、热量图、全国地区地理热力图,利用颜色的深浅表示密度或强度等场景(详细示例代码教程)

vue2,nuxt2,高德地图,热力图,绘制热力图层,报错,卡顿,出不来怎么办,无法关闭,怎么解决这个问题,出错,城市名称,城市某个街道,城区,省份,市区,县城,乡镇,村,各省份的热力图,交通流量,人口流量,密度,旅游出行,人口数量,颜色介绍,深浅代表,人流量,vue2使用高德地图实现某个区域热力图,vue高德地图api加载热力图及关闭,接收热力图数据并渲染热力图,关掉热力图,Vue使用高德API制作热力图,热力图加载不出来,电子围栏围住热力图,vue2中使用高德地图渲染热力图组件,高德地图使用热力图详细教

✅ 精选推荐文章 4201

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

线程(五)线程的同步和互斥——线程信号量

本文介绍了线程同步和互斥的几种方法,包括互斥锁、读写锁、自旋锁、条件变量和信号量。通过具体案例展示了这些机制在实际应用中如何实现线程间的同步与互斥。文章详细讲解了信号量的初始化、销毁以及加减操作,并通过实例展示了如何使用信号量控制线程执行顺序和实现线程间互斥与同步。最后,讨论了死锁的产生原因,包括资源竞争、持有并等待以及不可剥夺性,帮助读者深入理解线程同步与互斥的复杂性和重要性。

m0_52867657的博客 1128

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

【Linux多线程】信号量以及线程池

本篇博客将会介绍Linux线程中的信号量以及线程池,完善生产者消费者模型

m0_74215144的博客 1097

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

线程同步——信号量

信号量(Semaphore)是一种在多线程(或多进程)编程中用于控制对共享资源访问的同步机制。信号量主要用于解决并发访问和资源共享的问题,确保在任一时刻最多只有一个线程可以访问特定的资源。

weixin_75197906的博客 1066

进程、线程、信号量和互斥锁

进程、线程、信号量和互斥锁 本文转自博客《进程与线程的一个简单解释》 计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。 假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。 进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。 一个车间里,可以有很多工人。他们协同完成一个任务。 线程就好比车间里的工人。一

魔法师の屋子★↓☆ 3008

了解信号量Semaphore和线程池的差异

一、首先要明白Semaphore和线程池各自是干什么? 信号量Semaphore是一个并发工具类,用来控制可同时并发的线程数,其内部维护了一组虚拟许可,通过构造器指定许可的数量,每次线程执行操作时先通过acquire方法获得许可,执行完毕再通过release方法释放许可。如果无可用许可,那么acquire方法将一直阻塞,直到其它线程释放许可。 线程池用来控制实际工作的线程数量,通过线程复用的方...

mryang125的博客 1万+

Linux :线程【信号量】

信号量概念,模拟实现环形队列的生产消费模型。

2301_82256131的博客 1129

线程和信号量

1.线程 例360 thread2 注意: 新创建的线程不要使用2dx的内存管理(release, retain)等,也不要调用引擎与节目绘制相关的函数(OpenGL不支持多线程)。     其他读取数据和网络通信可以使用。 1.static pthread_t  f1_thread; 2./*线程地址、创建线程的参数 、此线程执行函数、指针参数(不传参数时为null)*/

renzhe20092584的专栏 669

C# 线程 6:线程同步——信号量和互斥体

[C# 线程处理系列]专题六:线程同步——信号量和互斥体 也不多说了,直接进入主题了 一、信号量(Semaphore) 信号量(Semaphore)是由内核对象维护的int变量,当信号量为0时,在信号量上等待的线程会堵塞,信号量大于0时,就解除堵塞。当在一个信号量上等待的线程解除堵塞时,内核自动会将信号量的计数减1。在.net 下通过Semaphore类来实现信号量同步。 Semaphor...

WarGames_dc的博客 1380
上一篇: 指南:工作者线程和信号量
下一篇: 编程的偏执
01Soft
博客等级 码龄24年 0粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值