您当前的位置:首页 > 电脑百科 > 程序开发 > 架构

linux c编程之高效线程池如何实现无琐化

时间:2020-08-24 12:45:27  来源:  作者:

大多数线程池实现都离不开锁的使用,如互斥量pthread_mutex*结合条件变量pthread_cond*。众所周知,锁的使用对于程序性能影响较大,虽然现有的pthread_mutex*在锁的申请与释放方面做了较大的优化,但是,线程池的实现是可以做到无锁化的。

1.常见线程池实现原理

linux c编程之高效线程池如何实现无琐化

 

如上图所示,工作队列由主线程和工作者线程共享,主线程将任务放进工作队列,工作者线程从工作队列中取出任务执行。共享工作队列的操作需在互斥量的保护下安全进行,主线程将任务放进工作队列时若检测到当前待执行的工作数目小于工作者线程总数,则需使用条件变量唤醒可能处于等待状态的工作者线程。当然,还有其他地方可能也会使用到互斥量和条件变量,不再赘述。

2.无锁化线程池实现原理

linux c编程之高效线程池如何实现无琐化

 

为解决无锁化的问题,需要避免共享资源的竞争,因此将共享工作队列加以拆分成每工作线程一个工作队列的方式。对于主线程放入工作和工作线程取出任务的竞争问题,可以采取环形队列的方式避免。在解决了锁机制之后,就只剩下条件变量的问题了,条件变量本身即解决条件满足时的线程通信问题,而信号作为一种通信方式,可以代替之,其大体编程范式为:

sigemptyset (&oldmask);
sigemptyset (&signal_mask);
sigaddset (&signal_mask, SIGUSR1);
rc = pthread_sigmask(SIG_BLOCK, &signal_mask, NULL);
if (rc != 0) {
    debug(TPOOL_ERROR, "SIG_BLOCK failed");
    return -1;
}
...

while (!condition) {
    rc = sigwait (&signal_mask, NULL);
    if (rc != 0) {
        debug(TPOOL_ERROR, "sigwait failed");
        return -1;
    }
}

rc = pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
if (rc != 0) {
    debug(TPOOL_ERROR, "SIG_SETMASK failed");
    return -1;
}

需要C/C++ linux服务器架构师学习资料私信“资料”(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQLredis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

linux c编程之高效线程池如何实现无琐化

 

3.无锁化线程池具体实现

在无锁线程池中,区别于常见线程池的地方主要在于信号与条件变量、任务调度算法、增加或减少线程数目后的任务迁移,另外还有一点就是环形队列的实现参考了Linux内核中的kfifo实现。

(1) 信号与条件变量

信号与条件变量的区别主要在于条件变量的唤醒(signal)对于接收线程而言可以忽略,而在未设置信号处理函数的情况下信号的接收会导致接收线程甚至整个程序的终止,因此需要在线程池产生线程之前指定信号处理函数,这样新生的线程会继承这个信号处理函数。多线程中信号的发送主要采用pthread_kill,为避免使用其他信号,本程序中使用了SIGUSR1。

(2) 任务调度算法

常见线程池实现的任务调度主要在操作系统一级通过线程调度实现。考虑到负载均衡,主线程放入任务时应采取合适的任务调度算法将任务放入对应的工作者线程队列,本程序目前已实现Round-Robin和Least-Load算法。Round-Robin即轮询式地分配工作,Least-Load即选择当前具有最少工作的工作者线程放入。

(3) 任务迁移

在线程的动态增加和减少的过程中,同样基于负载均衡的考量,涉及到现有任务的迁移问题。负载均衡算法主要基于平均工作量的思想,即统计当前时刻的总任务数目,均分至每一个线程,求出每个工作者线程应该增加或减少的工作数目,然后从头至尾遍历,需要移出工作的线程与需要移入工作的线程执行任务迁移,相互抵消。最后若还有多出来的工作,再依次分配。迁入工作不存在竞态,因为加入工作始终由主线程完成,而迁出工作则存在竞态,因为在迁出工作的同时工作者线程可能在同时执行任务。所以需要采用原子操作加以修正,其主要思想即预取技术,大致实现为:

do {
    work = NULL;
    if (thread_queue_len(thread) <= 0)  //also atomic
        break;
    tmp = thread->out;
    //prefetch work
    work = &thread->work_queue[queue_offset(tmp)];
} while (!__sync_bool_compare_and_swap(&thread->out, tmp, tmp + 1));
if (work) {
    // do something在线程的动态减少后,原先线程上未能执行完的任务只需要由
    //主线程再次根据任务调度算法重新分配至其他存活的工作者线程队列中即可,不
    //存在上述问题,当然,此时可以同时执行负载均衡算法加以优化。
}

(4) 环形队列

源码中环形队列实现主要参考了linux内核中kfifo的实现,如下图所示:

linux c编程之高效线程池如何实现无琐化

 

队列长度为2的整次幂,out和in下标一直递增至越界后回转,其类型为unsigned int,即out指针一直追赶in指针,out和in映射至FiFo的对应下标处,其间的元素即为队列元素。



Tags:线程池   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
又踩坑了系列&hellip; 得到反馈管理端一个返回系统基本权限数据的接口服务端异常了。查看错误日志: 竟然是OOM,Java应用程序已达到其可以启动线程数量的极限了。肯定是有地方...【详细内容】
2022-02-28  Tags: 线程池  点击:(172)  评论:(0)  加入收藏
"""@author: wangxingchun多线程(线程池)下载数据"""import requestsimport csvfrom concurrent.futures import ThreadPoolExecutor as tp#创建一个csv文件,注意创建writer对...【详细内容】
2022-02-08  Tags: 线程池  点击:(90)  评论:(0)  加入收藏
原文链接: https://mp.weixin.qq.com/s/MTw7z6n_wk4y4CTmGkoRoA一切要从CPU说起你可能会有疑问,讲多线程为什么要从CPU说起呢?原因很简单,在这里没有那些时髦的概念,你可以更加清...【详细内容】
2021-08-13  Tags: 线程池  点击:(163)  评论:(0)  加入收藏
多线程并发是Java语言中非常重要的一块内容,同时,也是Java基础的一个难点。说它重要是因为多线程是日常开发中频繁用到的知识,说它难是因为多线程并发涉及到的知识点非常之多,想...【详细内容】
2021-07-12  Tags: 线程池  点击:(176)  评论:(0)  加入收藏
1. Dubbo简介及线程池策略Apache Dubbo 是一款高性能、轻量级的开源 Java 服务框架。提供了六大核心能力:面向接口代理的高性能RPC调用,智能容错和负载均衡,服务自动注册和发现...【详细内容】
2021-05-18  Tags: 线程池  点击:(268)  评论:(0)  加入收藏
在上一篇文章C++使用socket实现与微信小程序通信(下)中,小懵白就给大家简要地讲解了线程池的原理。 今天呢,小懵白就给大家继续讲解C++如何实现封装线程池类。第一步首先,我们需...【详细内容】
2021-05-14  Tags: 线程池  点击:(277)  评论:(0)  加入收藏
见字如面,我是威哥,一个从普通二本院校毕业,从未曾接触分布式、微服务、高并发到通过技术分享实现职场蜕变,成长为RocketMQ社区优秀布道师、大厂资深架构师,出版《RocketMQ技...【详细内容】
2021-03-31  Tags: 线程池  点击:(306)  评论:(0)  加入收藏
作者公众号:一角钱技术(org_yijiaoqian)前言线程池的具体实现有两种,分别是ThreadPoolExecutor 默认线程池和ScheduledThreadPoolExecutor 定时线程池,上一篇已经分析过ThreadPoo...【详细内容】
2020-12-22  Tags: 线程池  点击:(179)  评论:(0)  加入收藏
之前我们介绍了线程池的四种拒绝策略,了解了线程池参数的含义,那么今天我们来聊聊Java 中常见的几种线程池,以及在jdk7 加入的 ForkJoin 新型线程池 首先我们列出Java 中的...【详细内容】
2020-11-05  Tags: 线程池  点击:(102)  评论:(0)  加入收藏
前面几篇文章分析了线程的主要实现,今天来整体总结以下他们。总览图直接上总结的总览图,如下图: 如果看过前几篇文章应该基本能够看懂这张总结图,可能在单独的一篇文章里弄懂了...【详细内容】
2020-09-08  Tags: 线程池  点击:(83)  评论:(0)  加入收藏
▌简易百科推荐
微服务,顾名思义,就是将我们程序拆分为最小化单元来提供服务。在一体化系统中,各个微服务也是不可能独立存在的,那么微服务之间涉及到的数据依赖问题,应该怎么处理呢?我们从场景入...【详细内容】
2022-07-06  老周聊架构谈人生    Tags:微服务   点击:(17)  评论:(0)  加入收藏
本文笔者来为大家介绍下Netty的核心引擎Reactor的运转架构,希望通过本文的介绍能够让大家对Reactor是如何驱动着整个Netty框架的运转有一个全面的认识。也为我们后续进一步...【详细内容】
2022-07-05  Java码农之路    Tags: Reactor   点击:(17)  评论:(0)  加入收藏
简介SqlRunner,一个非常实用的、用于操作数据库的工具类。该类对JDBC进行了很好的封装,结合SQL工具类,能够很方便地通过Java代码执行SQL语句并检索SQL执行结果。SqlRunner提供...【详细内容】
2022-07-05  嗨皮汪小成    Tags:SqlRunner   点击:(19)  评论:(0)  加入收藏
以下文章来源于信息化与数字化 ,作者沈旸 来源:信息化与数字化导读:熟悉SAP ERP的同学可以从后往前看,有精彩的历史故事。 “开源”对企业应用和生态有什么样的影响? 在Github上...【详细内容】
2022-07-05  数字化企业    Tags:架构   点击:(16)  评论:(0)  加入收藏
自从去年 10 月份搜狗正式被腾讯合并以后,我一直想给大家讲讲腾讯内部目前开发在用的一些技术栈,我想这对同学们有很高的学习价值。但苦于公司内部有明确的规定,不允许私自对外...【详细内容】
2022-06-30  Java高级互联小课堂    Tags:微服务架构   点击:(21)  评论:(0)  加入收藏
2022-06-22  老傅解码  今日头条  Tags:混合云   点击:(24)  评论:(0)  加入收藏
采访嘉宾 | 蔡超、成国柱、谭待编辑|marsxxl在 InfoQ 成立 15 周年之际,InfoQ 编辑部发起了“2007-2022:云、运维、架构、前端的 15 年演进史”特别策划,将和业内专家共同盘点云...【详细内容】
2022-06-21  InfoQ    Tags:软件架构   点击:(32)  评论:(0)  加入收藏
2022-06-20  开猿节流    Tags:架构   点击:(28)  评论:(0)  加入收藏
微服务架构的数据一致性微服务架构下,最好的分布式数据一致性解决方案就是尽量避免分布式事务,然而,在很多场景下,分布式事务是难以避免的。在金融、电信领域中,很多业务场景要求...【详细内容】
2022-06-16  程序员高级码农II    Tags:微服务架构   点击:(33)  评论:(0)  加入收藏
想知道如何设计大型企业级的系统吗?在开始主要的代码开发之前,我们必须选择一种合适的体系架构,它将为我们提供所需的功能和质量属性。因此,在将它们应用到我们的设计之前,应该先...【详细内容】
2022-06-16  老傅解码    Tags:架构模式   点击:(33)  评论:(0)  加入收藏
站内最新
站内热门
站内头条