有时我们希望把一部分工作通过创建线程的方式异步执行,这样我们可以在执行任务的同时,继续执行其他任务。但是如果这种需求比较多的话,频繁的创建和销毁线程带来很大的性能损耗。如果我们能创建一个或一些线程,然后重复使用它们,就可以避免这个问题。

Qemu的实现

qemu模仿glib实现了线程池的功能,目前qemu中线程池主要应用在raw文件的支持上,当linux-aio不可用时,就像glibc,通过线程实现aio机制。我们也可看到,代表线程池中的线程成员的数据结构 ThreadPoolElement 就包含了用来描述aio的BlockAIOCB结构。相关数据结构如下:

首先看一下线程池相关的数据结构:

typedef struct AIOCBInfo {

void (*cancel_async)(BlockAIOCB *acb);

AioContext *(*get_aio_context)(BlockAIOCB *acb);

size_t aiocb_size;

} AIOCBInfo;

struct BlockAIOCB {

const AIOCBInfo *aiocb_info;

BlockDriverState *bs;

BlockCompletionFunc *cb;

void *opaque;

int refcnt;

};

struct ThreadPoolElement {

BlockAIOCB common;

ThreadPool *pool;

ThreadPoolFunc *func;

void *arg;

/* Moving state out of THREAD_QUEUED is protected by lock.  After

* that, only the worker thread can write to it.  Reads and writes

* of state and ret are ordered with memory barriers.

*/

enum ThreadState state;

int ret;

/* Access to this list is protected by lock.  */

QTAILQ_ENTRY(ThreadPoolElement) reqs;

/* Access to this list is protected by the global mutex.  */

QLIST_ENTRY(ThreadPoolElement) all;

};

struct ThreadPool {

AioContext *ctx;

QEMUBH *completion_bh;

QemuMutex lock;

QemuCond worker_stopped;

QemuSemaphore sem;

int max_threads;

QEMUBH *new_thread_bh;

/* The following variables are only accessed from one AioContext. */

QLIST_HEAD(, ThreadPoolElement) head;

/* The following variables are protected by lock.  */

QTAILQ_HEAD(, ThreadPoolElement) request_list;

int cur_threads;

int idle_threads;

int new_threads;     /* backlog of threads we need to create */

int pending_threads; /* threads created but not running yet */

bool stopping;

};

ThreadPool 数据结构负责维护线程池里面的线程成员,线程的创建是通过下半部来实现的;ThreadPool 中有5个负责维护不同状态下的线程成员的计数器,max_threads负责统计线程池中允许创建的线程的最大值; new_threads负责统计需要创建的线程数;pending_threads负责统计已创建但还没有运行的线程数;idle_threads负责统计空闲的线程数;cur_threads负责统计当前线程池中线程的个数;注意cur_threads包含new_threads中尚未创建的线程。

线程池创建

首先通过thread_pool_new函数为特定的AioContext实例创建一个新的线程池。在这个函数中初始化ThreadPool数据结构的各个成员,包括负责创建新线程的new_thread_bh和线程执行完毕后用来调度执行任务完成回调函数的completion_bh。

任务提交

我们通过调用thread_pool_submit_aio函数来提交任务,这个函数的原型是:

BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool,

ThreadPoolFunc *func, void *arg,

BlockCompletionFunc *cb, void *opaque)

这个函数为提交的任务创建一个ThreadPoolElement实例添加到ThreadPool中,同时调用spawn_thread函数来创建一个qemu线程。spawn_thread函数是通过调度pool->new_thread_bh来创建qemu线程的。

线程执行

创建的线程执行worker_thread函数,这个函数从pool->request_list链表中取下第一个ThreadPoolElement节点,执行其任务函数,然后调度执行pool->completion_bh;这个bh遍历pool->head链表,根据其ThreadPoolElement成员的状态来决定是否执行实例上注册的完成回调函数。

线程执行完一个任务后,也就是一个ThreadPoolElement实例被执行后,线程就出在idle状态,等待下一个任务提交动作。任务提交与线程执行之间的同步是通过pool->sem来实现的。thread_pool_submit_aio中任务提交后会调用qemu_sem_post(&pool->sem)来增加pool->sem的计数,worker_thread在pool->sem上醒来后从pool->request_list链表上获取下一个要执行的ThreadPoolElement节点。

总结

线程池计数提供了线程重复使用的功能,这在qemu有大量io操作的时候提高了性能;同时,也提供了除了linux-aio之外的aio实现。

参考:

https://developer.gnome.org/glib/2.46/glib-Thread-Pools.html

Qemu线程池介绍的更多相关文章

  1. 【转】JUC下面线程池介绍

    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new T ...

  2. ThreadPoolExecutor之二:jdk实现的线程池介绍

    一 简介 线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的.在jdk1.5之后这一情况有了很大的改观.Jdk1.5之后加入了java.uti ...

  3. 内存池、进程池、线程池介绍及线程池C++实现

    本文转载于:https://blog.csdn.net/ywcpig/article/details/52557080 内存池 平常我们使用new.malloc在堆区申请一块内存,但由于每次申请的内存 ...

  4. JDK自带线程池介绍及使用环境

    1.newFixedThreadPool创建一个指定工作线程数量的线程池.每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中. 2.newCach ...

  5. ThreadPool(线程池)介绍

    >>返回<C# 并发编程> 1. 线程池的由来 1.1. 线程池出现前 1.2. 线程池的诞生 1.3. CLR线程池工作过程 2. 线程池解决的问题 2.1. 异步调用方法 ...

  6. 线程池是什么?Java四种线程池的使用介绍

    使用线程池的好处有很多,比如节省系统资源的开销,节省创建和销毁线程的时间等,当我们需要处理的任务较多时,就可以使用线程池,可能还有很多用户不知道Java线程池如何使用?下面小编给大家分享Java四种线 ...

  7. Android线程池(一)

    本篇文章主要介绍Android自带的线程池的使用. 首先要引入线程池的概念 线程池:是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务. 线程池线程都是后台线程.每个线 ...

  8. Java线程池与java.util.concurrent

    Java(Android)线程池 介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行 ...

  9. 在Linux下写一个线程池以及线程池的一些用法和注意点

    -->线程池介绍(大部分来自网络)  在这个部分,详细的介绍一下线程池的作用以及它的技术背景以及他提供的一些服务等.大部分内容来自我日常生活中在网络中学习到的一些概念性的东西. -->代码 ...

随机推荐

  1. ()java jdbc连接

    测试使用 jdk-8u191-windows-x64.mysql-8.0.12-winx64.mysql-connector-java-8.0.13.jar 查询 import java.sql.*; ...

  2. 两个imageView实现图片轮播

    前言 在不少的项目中,都会用到图片轮播这个功能,现在网上关于图片轮播的轮子也层出不穷,千奇百怪,笔者根据自己的思路,用两个imageView也实现了图片轮播,这里给大家介绍笔者的主要思路以及大概步骤. ...

  3. [BZOJ 1806] Miners 矿工配餐

    Link: BZOJ 1806 传送门 Solution: 为了使状态包含每个节点前所有必须的信息: 设$dp[i][a1][a2][b1][b2]$为配送到第$i$个,一厂前两个为$a1,a2$,二 ...

  4. [TJOI2009] 战争游戏

    题目背景 小R正在玩一个战争游戏.游戏地图是一个M行N列的矩阵,每个格子可能是障碍物,也可能是空地,在游戏开始时有若干支敌军分散在不同的空地格子中.每支敌军都可以从当前所在的格子移动到四个相邻的格子之 ...

  5. Excel设置下拉菜单并隐藏下拉菜单来源单元格内容

    一.问题来源 做实验室的进展统计表,老师让加上开始时间和完成时间,时间格式:周几_上午(下午.晚上). 这样就可以了做下拉菜单,方便填写,而且格式统一,方便查看. 二.解决办法 2.1 下来菜单 红框 ...

  6. Winform打砖块游戏制作step by step第7节---碰撞检测

    一 引子 为了让更多的编程初学者,轻松愉快地掌握面向对象的思考方法,对象继承和多态的妙用,故推出此系列随笔,还望大家多多支持. 预备知识,无GDI画图基础的童鞋请先阅读一篇文章让你彻底弄懂WinFor ...

  7. cocurrent包semaphore信号量

    semaphore英[ˈseməfɔ:(r)]美[ˈsɛməˌfɔr, -ˌfor]n. 臂板信号系统,(铁道)臂板信号装置; Semaphore 用法 信号量主要有两种用途: 保护一个重要(代码)部 ...

  8. ECSHOP生成缩略图模糊

    原因是因为ECSHOP生成缩略图时,用到的函数 imagejpeg()  没有设置质量参数.注释:质量参数为可选项,范围从 0(最差质量,文件更小)到 100(最佳质量,文件最大).如果没有设置质量参 ...

  9. 你今天Restful了吗?

    大家都宣称自己已经满足REST架构的风格, 那到底需要怎么去评价是否符合REST架构, 至少得有以下几个特征. REST 的约束包括: 无状态.在不同的客户端请求之间,服务器并不保存客户端相关的上下文 ...

  10. PS 不能使用移动工具 因为目标图层被隐藏怎么办

    photoshop任何图层都无法移动或者修改,提示目标通道被隐藏. 在ps任何图层都无法移动并且无法使用橡皮擦等修改工具,提示目标通道被隐藏.我看了下通道里面多了一个快速蒙版层(并且是被隐藏的),我打 ...