glib库线程池代码分析
- 格式:doc
- 大小:90.00 KB
- 文档页数:9
使⽤GDB调试多线程实例详解
先写⼀段多线程程序。
makefile
加上 -g参数⽣成可调式信息,可以进⾏调试。
pthread不是Linux下的默认的库,也就是在链接的时候,⽆法找到phread库中哥函数的⼊⼝地址,于是链接会失败。
在gcc编译的时候,附加要加 -lpthread参数即可解决。
gdb test 进⼊调试
需要调试的地⽅打下断点,run运⾏到断点处。
r 运⾏到断点处,info thread可以查看被调试的线程。
thread apply all bt 让所有线程打印堆栈信息
set scheduler-locking off|on|step
在使⽤step或continue命令调试当前被调试线程的时候,其他线程也是同时执⾏的,如果我们只想要被调试的线程执⾏,⽽其他线程停⽌等待,那就要锁定要调试的线程,只让它运⾏。
off:不锁定任何线程,所有线程都执⾏。
on:只有当前被调试的线程会执⾏。
step:阻⽌其他线程在当前线程单步调试的时候抢占当前线程。
只有当next、continue、util以及finish的时候,其他线程才会获得重新运⾏的。
show scheduler-locking:
这条命令是为了查看当前锁定线程的模式。
感谢阅读,希望能帮助到⼤家,谢谢⼤家对本站的⽀持!。
线程池原理详解随着计算机的不断发展,多线程编程已经成为了现代软件开发中不可或缺的一部分。
然而,线程的创建和销毁是一项昂贵的操作,特别是在需要频繁创建和销毁线程的情况下,这种开销会变得非常显著。
为了解决这个问题,线程池应运而生。
本文将深入探讨线程池的基本原理、实现方式以及使用场景。
什么是线程池?线程池是一种预先创建了一组线程,以便在需要时可以重用它们的技术。
线程池中的线程在任务完成后不会立即销毁,而是等待下一次任务的到来。
这样可以避免频繁创建和销毁线程所带来的开销,从而提高系统的性能。
线程池的基本原理线程池的基本原理就是将一组线程预先创建好,然后在需要执行任务时将任务提交到线程池中执行。
线程池中的线程会不断地从任务队列中取出任务并执行。
当任务执行完毕后,线程不会立即销毁,而是继续等待下一次任务的到来。
这样可以减少线程的创建和销毁次数,从而提高系统的性能。
线程池的实现方式线程池的实现方式有多种,下面介绍两种常用的实现方式。
1. 固定大小线程池固定大小线程池是一种最简单的线程池实现方式。
在创建线程池时,指定线程池中的线程数量。
线程池中的线程会不断地从任务队列中取出任务并执行。
当任务执行完毕后,线程会继续等待下一次任务的到来。
如果任务队列中没有任务,则线程会一直等待,直到有任务到来。
固定大小线程池的优点是可以控制线程的数量,从而避免线程过多导致系统资源的浪费。
缺点是当任务数量超过线程池中线程的数量时,任务会被阻塞,等待线程执行完毕。
2. 动态大小线程池动态大小线程池是一种更加灵活的线程池实现方式。
在创建线程池时不需要指定线程的数量。
线程池中的线程数量会根据任务的数量自动调整。
当任务数量增加时,线程池中的线程数量也会增加。
当任务数量减少时,线程池中的线程数量也会减少。
动态大小线程池的优点是可以根据任务的数量自动调整线程的数量,从而避免线程过多导致系统资源的浪费。
缺点是线程数量的动态调整会增加一定的开销,对系统的性能有一定的影响。
线程池源码分析概述在 java 中,线程池 ThreadPoolExecutor 是⼀个绕不过去的类,它是享元模式思想的体现,通过在容器中创建⼀定数量的线程加以重复利⽤,从⽽避免频繁创建线程带来的额外开销。
⼀个设置合理的线程池可以提⾼任务响应的速度,并且避免线程数超过硬件能⼒带来的意外情况。
在本⽂,将深⼊线程池源码,了解线程池的底层实现与运⾏机制。
⼀、构造⽅法ThreadPoolExecutor 类⼀共提供了四个构造⽅法,我们基于参数最完整构造⽅法了解⼀下线程池创建所需要的变量:public ThreadPoolExecutor(int corePoolSize, // 核⼼线程数int maximumPoolSize, // 最⼤线程数long keepAliveTime, // ⾮核⼼线程闲置存活时间TimeUnit unit, // 时间单位BlockingQueue<Runnable> workQueue, // ⼯作队列ThreadFactory threadFactory, // 创建线程使⽤的线程⼯⼚RejectedExecutionHandler handler // 拒绝策略) {}核⼼线程数:即长期存在的线程数,当线程池中运⾏线程未达到核⼼线程数时会优先创建新线程;最⼤线程数:当核⼼线程已满,⼯作队列已满,同时线程池中线程总数未超过最⼤线程数,会创建⾮核⼼线程;⾮核⼼线程闲置存活时间:当⾮核⼼线程闲置的时的最⼤存活时间;时间单位:⾮核⼼线程闲置存活时间的时间单位;任务队列:当核⼼线程满后,任务会优先加⼊⼯作队列,等等待核⼼线程消费;线程⼯⼚:线程池创建新线程时使⽤的线程⼯⼚;拒绝策略:当⼯作队列与线程池都满时,⽤于执⾏的策略;⼆、线程池状态1.线程池状态线程池拥有⼀个 AtomicInteger 类型的成员变量 ctl ,通过位运算分别使⽤ ctl 的⾼位低位以便在⼀个值中存储线程数量以及线程池状态。
glib函数说明GLib函数说明简介GLib是一个开源的C函数库,提供了许多用于操作字符串、数据结构、文件系统等常见任务的函数。
它起初是为GTK+图形库而开发的,但现在已经被广泛使用于许多其他项目中。
常用函数g_strdup•函数描述:复制一个字符串并返回一个指向新字符串的指针。
•函数原型:gchar* g_strdup (const gchar *str);•示例代码:const char *source = "Hello, world!";char *destination = g_strdup(source);•返回值:返回一个指向复制字符串的指针,如果内存不足则返回NULL。
•注意事项:使用完后需要手动释放返回的指针。
g_list_append•函数描述:在链表的末尾添加一个元素。
•函数原型:GList* g_list_append (GList *list, gpointer data);•示例代码:GList *list = NULL; // 初始为空链表list = g_list_append(list, "Alice");list = g_list_append(list, "Bob");•返回值:返回添加了元素后的链表的指针。
•注意事项:需要传入链表的指针和要添加的元素的指针。
g_hash_table_new•函数描述:创建一个新的哈希表。
•函数原型:GHashTable* g_hash_table_new (GHashFunc hash_func, GEqualFunc key_equal_func);•示例代码:GHashTable *hash_table = g_hash_table_new(g_str_has h, g_str_equal);•返回值:返回指向新创建的哈希表的指针。
•注意事项:需要传入计算哈希值的函数和判断键值相等的函数。
线程池创建参数详解线程池(Thread Pool)是一种多线程处理的机制,它可以管理和复用多个线程来执行一组任务,提高系统的效率和性能。
在创建线程池时,我们可以根据实际需求来设置一些参数,以满足不同的场景和要求。
本文将详细介绍线程池的创建参数及其作用,帮助读者更好地理解和使用线程池。
线程池的基本参数创建线程池时,通常需要设置以下基本参数:•核心线程数(corePoolSize):线程池中所能容纳的核心线程数,即一直存活的线程数量。
当有新任务提交到线程池时,线程池会创建一个新的线程来处理,直到达到核心线程数。
•最大线程数(maximumPoolSize):线程池中所能容纳的最大线程数,在需要处理的任务过多时,线程池可以扩展到最大线程数来处理任务。
超过最大线程数的任务将会被阻塞或拒绝。
•任务队列(workQueue):用于存放等待执行的任务的队列。
当任务提交到线程池时,如果核心线程数已满,任务将被放入任务队列中等待执行。
•线程存活时间(keepAliveTime):当线程池中线程数量超过核心线程数时,多余的线程在空闲一段时间后会被销毁,以避免资源浪费。
线程存活时间即空闲线程的最大存活时间。
•时间单位(unit):线程存活时间的单位,通常为秒、毫秒等。
任务拒绝策略当线程池无法继续接受新任务时,可以采用不同的任务拒绝策略。
线程池提供了几种常用的任务拒绝策略:•AbortPolicy:默认的任务拒绝策略,当线程池无法继续接受新任务时,会抛出RejectedExecutionException异常。
•CallerRunsPolicy:如果线程池无法接受新任务,会将任务退回给提交任务的线程,由提交任务的线程执行该任务。
•DiscardPolicy:当线程池无法接受新任务时,会直接丢弃该任务,不抛出任何异常。
•DiscardOldestPolicy:当线程池无法接受新任务时,会丢弃队列中最早的一个任务,然后重新尝试执行该任务。
glib手册是一个重要的参考资源,它提供了关于glib库的详细信息和用法指南。
glib库是一个广泛使用的C语言库,用于提供各种与内存管理、字符串处理、文件I/O和其他常见任务相关的功能。
以下是对glib手册的简要概述,以500-800字回答:glib库是一组用于C语言编程的库,它提供了许多实用的功能,包括内存管理、字符串处理、文件I/O和其他常见的任务。
glib库的主要目的是为了提供高效、灵活和易于使用的工具,以便开发人员能够更轻松地编写跨平台的代码。
glib库包含许多功能,其中包括:* 内存管理:glib提供了用于分配和释放内存的函数,以及用于跟踪内存使用情况的工具。
* 字符串处理:glib提供了用于处理字符串的函数,包括字符串连接、格式化输出、查找和替换等。
* 文件I/O:glib提供了用于读取和写入文件的函数,以及用于处理文件路径和文件锁的函数。
* 线程和同步:glib提供了用于创建和管理线程的函数,以及用于同步和事件处理的工具。
要使用glib库,您需要了解其基本概念和功能。
以下是使用glib库的一般步骤:1. 包含glib头文件:在您的代码中包含glib头文件,以便可以使用库中的函数和数据类型。
2. 初始化glib:在使用glib库之前,您需要调用一些初始化函数来设置库的状态。
3. 分配内存:使用glib提供的函数来分配内存,以便您可以在程序中存储数据。
4. 使用函数:使用glib提供的函数来完成您的任务,例如字符串处理、文件I/O等。
5. 释放内存:在使用完内存后,您需要使用glib提供的函数来释放它,以避免内存泄漏。
6. 清理glib:在程序结束时,您需要调用一些清理函数来释放glib的状态并关闭库。
总的来说,glib手册是一个非常有用的资源,它提供了关于glib库的详细信息和用法指南。
通过阅读手册并遵循其中的建议,您可以更好地了解如何使用glib库来编写高效、灵活和易于维护的代码。
glib 类实例glib是一种功能强大的C语言库,用于简化和加速开发过程。
它提供了许多用于处理常见任务的函数和数据结构,使开发人员能够更轻松地创建高效和可靠的应用程序。
一、什么是glib?glib是一种开源的通用库,最初由GNOME项目开发,并被广泛用于Linux和Unix系统中。
它提供了许多常见任务的函数和数据结构,包括字符串处理、内存管理、列表和哈希表等。
glib还提供了一些高级功能,如线程、进程间通信和网络编程等。
它被设计为可移植和高效的,可以在各种操作系统上运行。
二、glib的特点1.跨平台性:glib可以在多个操作系统上运行,包括Linux、Unix、Windows等。
这使得开发人员可以轻松地将其应用程序移植到不同的平台上,而不需要重新编写代码。
2.高效性:glib使用了许多优化技术,以提高性能和效率。
它提供了高效的数据结构和算法,可以快速地执行各种任务。
此外,glib 还支持多线程编程,可以充分利用多核处理器的优势。
3.丰富的功能:glib提供了许多功能丰富的模块,可用于各种开发任务。
例如,glib提供了字符串处理模块,可以方便地进行字符串操作,如拼接、分割和比较等。
另外,glib还提供了内存管理模块,可以帮助开发人员更有效地管理内存资源。
4.易于使用:glib的API设计简洁明了,易于使用和理解。
它提供了详细的文档和示例代码,可以帮助开发人员快速上手。
此外,glib还有一个活跃的社区,开发人员可以在其中获取支持和帮助。
三、glib的应用领域glib在许多开源项目中被广泛使用,尤其是在GNOME桌面环境中。
许多GNOME应用程序使用glib来处理字符串、内存和数据结构等任务。
此外,glib还被一些商业应用程序使用,如Adobe的Acrobat Reader和Google的Chrome浏览器等。
在开发过程中,glib可以帮助开发人员提高工作效率,减少开发时间。
它提供了许多高级功能和工具,如线程和进程管理、网络编程和文件操作等。
GRBL源代码分析GRBL是一种开源的嵌入式数控运动控制软件,广泛应用于DIY桌面CNC机器人和3D打印机中。
以下是对GRBL源代码的详细分析,主要包括代码结构、主要功能模块和算法。
首先,GRBL的代码结构采用了面向对象的编程风格,主要分为核心模块、通信模块、插补模块和硬件驱动模块等。
核心模块是GRBL的主要模块,其中包含了程序的初始化、主循环、处理程序等。
这些代码负责将用户输入的G代码进行解析,并转换为控制机器运动的指令。
核心模块还包含了状态机,用于管理机器的状态和控制流程。
通信模块负责与外部环境进行通信,包括接收和发送数据。
GRBL支持通过串口和USB进行通信,这些代码负责处理接收到的指令,并将执行结果返回给用户。
插补模块是GRBL的一个重要模块,负责计算机器的运动轨迹。
GRBL 使用的是简化的线性插值算法,通过计算每个步进电机的速度和加速度来实现平滑的运动控制。
硬件驱动模块是GRBL的底层驱动代码,用于与硬件进行通信。
GRBL 支持多种不同型号的Arduino开发板和步进电机驱动器,这些代码负责与硬件进行交互,控制机器的运动。
GRBL的主要功能模块包括直线插补、圆弧插补、坐标变换和速度控制等。
直线插补模块负责计算两个点之间的直线轨迹。
GRBL使用简单的直线插值算法,按照每个步进电机的速度和加速度进行计算,实现平滑的直线运动。
圆弧插补模块负责计算两个点之间的圆弧轨迹。
GRBL使用Bresenham 算法来近似计算圆弧的插值点,然后按照直线插值的方法进行计算,实现平滑的圆弧运动。
坐标变换模块负责将用户指定的坐标系转换为机器的坐标系。
GRBL 支持多种坐标系,包括绝对坐标系和相对坐标系,在坐标变换模块中进行转换。
速度控制模块负责计算机器的速度和加速度。
GRBL使用梯形速度曲线来控制机器的加速和减速,通过调整速度和加速度参数,可以实现不同的运动效果。
总结起来,GRBL是一款功能强大的嵌入式数控运动控制软件,具有稳定性高、代码结构清晰、功能丰富等特点。
Dbus-glib使用方法说明一、背景介绍Phoenix平台从安全的角度考虑,广泛的使用DBUS进行进程间通讯。
1.优点:DBUS总线分为系统总线与会话总线两类,两者之前不能互相通信,所以任何应用程序不能欺骗系统事件,安全性很好。
2.缺点●直接使用Dbus标准接口调用很繁琐,且之前Phoenix平台没有统一的DBUS接口封装,各服务之间各写一套,不易维护也容易出错。
●接受方法调用端、消息接收端等程序需要非阻塞式(阻塞式的无法多线程DBUS通讯)判断是否接收到DBUS信息,形如:While(1){dbus_connection_read_write();msg = dbus_connection_borrow_message(conn);if (NULL == msg) {usleep(xxx);continue;}…}如上所示,多个服务同时运行的情况下,会占用大量CPU时间片,之前就有测试报告应用程序压力运行单一操作的情况下,应用程序会由快跑慢。
因此需要一个稳定可靠的DBUS调用封装,上层统一该封装接口进行DBUS通讯。
二、Dbus-glib介绍Dbus-glib是GNU标准库,在Dbus接口上封装,方便上层服务与应用更好的使用。
其形如一个DBUS代理服务器,由它进行所有DBUS消息的遍历与转发,服务端与消息发送端只需要向DBUS deamon申请注册唯一的DBUS name 、绑定GOBJECT后,DBUS deamon就会将申请连到到该DBUS name的DBUS信息转发给指定应用。
直接调用DBUS接口的构图如下:使用Dbus-glib结构图如下:从上图显示,所有的需要进行DBUS相互通讯的程序都只与Dbus daemon进行通讯。
函数调用流程:服务端申请一个GObject,绑定以下信息:Dbus name:A,Dbus object:BDbus interface:CMethod :D注册到dbus daemon中,其中D设置为回调函数客户端向dbus daemon申请调用注册信息为Dbus name:A,Dbus object:BDbus interface:C的D函数dbus daemon收到客户端的消息后,查询是否存在该注册信息的回调函数,如果找不到daemon会产生错误消息,作为应答消息给客户端。
glib库线程池代码分析(觉得不错)
2007年07月30日星期一下午 12:39
本文章主要讲了两部分内容:一是分析了异步队列的原理和实现,二是分析线程池的原理和实现。
在多线程程序的运行中,如果经常地创建和销毁执行过程相似而所用数据不同的线程,系统的效率,系统资源的利用率将会受到极大的影响。
对于这一问题可用类似glib库中的线程池的解决办法。
我们可以这样想像线程池的处理,当有新的数据要交给线程处理时,主程序/主线程就从线程池中找到一个未被使用的线程处理这新来的数据,如果线程池中没有找到可用的空闲线程,就新创建一个线程来处理这个数据,并在处理完后不销毁它而是把这个线程放到线程池中,以备后用。
线程池的这个原理和内存管理中slab机制有异曲同工之妙!我想无论是线程池的这种处理方式还是slab机制,其本质思想还是一致的。
近来做的项目,在框架中用到了多线程的异步队列,实现形式和glib中的异步队列极其相似,而glib线程池中的代码也用到了异步队列(名字用同步队列更合适),因此就先分析一下异步队列。
异步队列的概念是这样的:所有的数据组织成队列,供多线程并发访问,而这些并发控制全部在异步队列里面实现,对外面只提供读写接口;当队列中的数据为空时,如果是读线程访问异步队列,那么这一读线程就等待,直到有数据为止;写线程向队列放数据时,如果有线程在等待数据就唤醒等待线程。
异步队列主要代码剖析:
有了前面的异步队列基础就可以分析线程池是怎么实现的了。
在glib库中的线程池的实现和使用有两种方式:1. 单个线程池对象不共享方式;2. 多个线程池对象共享线程方式,也即把各个具体的线程池对象创建的把任务做完了的线程统一放在全局线程池中进行统一管理,各个具体的线程池对象要使用线程时,可以先向全局线程池中取线程,如果全局线程池没有线程了具体的线程池对象就可自行创建线程。
线程池的数据结构有两部分,一部分是在头文件中,另一部分在C文件中。
这是C语言中常用的信息隐藏方法之一,把要暴露给用户的数据放在头文件中,而要隐藏的数据则放在C文件中。
下面是线程池头文件中的数据结构:
我们可以先来分析单个线程对象不共享的主要实现。
在分析它的实现之前,可以先看看一个流程图
从上图可见当主线程有数据交给线程池处理时,只要调用异步队列相关的push接口,线程池中的任何一个线程都可以为这服务。
根据以上的流程图看看单个线程对象不共享方式的主要实现代码,它的调用从创建线程池对象开始:
g_thread_pool_new--->g_thread_pool_start_thread--->
g_thread_create(g_thread_pool_thread_proxy,pool,FALSE,&local_error)--->>g_threa
现在可以结合流程图分析线程池中创建一个线程的一个情景:从函数
g_thread_pool_new的while循环调用了 g_thread_pool_start_thread函数,在函数中直接调用g_thread_create创建线程,被创建的线程调用函数
g_thread_pool_wait_for_new_task循环等待任务的到来,函数
g_thread_pool_wait_for_new_task调用g_async_queue_pop_unlocked
(pool->queue)真正进入等待。
如此可知,最终新创建的线程是调用异步队列的pop接口进入等待状态的,这样一个线程的创建就大功告成了。
而函数
g_thread_pool_new的while循环结束时就创建了max_threads个等待线程,也即这个新建的线程池对象有了 max_threads个线程以备使用。
创建线程池、线程池中的线程是为了使用它,在线程池中取线程,叫线程干活的过程就很简单多了,这个调用过程:g_thread_pool_push--à
g_thread_pool_queue_push_unlocked--à g_async_queue_push_unlocked。
可见最终调用的是异步数据队列的push接口,把要处理的数据插入队列后它就会唤醒等待异步队列数据的等待线程。
总结:单个线程池对象不共享方式在管理多线程时是以线程池对象中的异步队列为中心,新创建的线程或做完任务的线程并不释放,让它调用异步队列的pop接口进入等待状态,而在使用唤醒线程池中的线程就是调用异步队列的push接口。