简介

线程池是什么?

  1. 打饭的阿姨们
  2. 前去吃饭的人们,任务
  3. 管理组件

线程池由三部分组成

  1. 执行队列,线程s
  2. 任务队列,任务s
  3. 管理组件

类似于

  1. 银行营业厅
  2. 食堂打饭

    每个打饭的人都是一个线程

    管理制度

参考链接

https://www.zhihu.com/question/27908489/answer/355105668

https://www.bilibili.com/video/BV1AT4y13791?from=search&seid=3536843546637261551

https://github.com/lizhenghn123/zl_threadpool (github点赞数较多)

线程池解决什么问题

  1. 解决任务处理。
  2. 阻塞IO。
  3. 解决线程创建于销毁的成本问题。
  4. 管理线程。
  5. 异步解耦的作用。

问题

  1. 如何增加线程
  2. 如何减少线程
  3. 增加与减少的策略

C的实现策略

首先我们来认识这些多线程要用的pthread接口函数

pthread_mutex_lock(&pool->jobs_mutex); // 对资源上锁

pthread_mutex_unlock(&pool->jobs_mutex); // 对资源解锁

pthread_cond_signal(&pool->jobs_cond); // 每调用一次,相当于P操作

pthread_cond_wait(&worker->pool->jobs_cond, &worker->pool->jobs_mutex); // 每调用一次相当于V操作

简单来说条件变量就是,许可证的发放,P相当于发了一张许可证,V相当于销毁了一张许可证,当没有许可证的时候pthread_cond_wait函数阻塞



pthread_create(pthread_t *thread, const pthread_attr_t *attr,

void (start_routine) (void *), void *arg);

attr 如果为NULL表示的是默认的属性,start_routine表示函数指针,指向默认的线程函数,arg表示线程函数的唯一的参数

pthread_exit(NULL); // 退出线程,线程销毁操作

线程池实现源代码

code

#include <pthread.h>
#include <stdio.h>
#include<string.h>
#include <stdlib.h>
#include <unistd.h>
#include "timee.hh"
// B 站 线程池
// head insert
#define LL_ADD(item, list) do { \
item->prev = NULL; \
item->next = list; \
if(list != NULL) \
list->prev = item; \
list = item; \
} while(0) #define LL_REMOVE(item, list) do { \
if(item->prev != NULL) item->prev->next = item->next; \
if(item->next != NULL) item->next->prev = item->prev; \
if(list == item) list = item->next; \
item->prev = item->next = NULL; \
} while(0) struct NWORKER{
pthread_t thread;
struct NMANAGER *pool;
int terminate;
struct NWORKER *prev;
struct NWORKER *next;
}; struct NJOB{
void (*func)(struct NJOB *job);
void *user_data;
struct NJOB *prev;
struct NJOB *next;
}; struct NMANAGER {
struct NWORKER *workers;
struct NJOB *jobs; pthread_cond_t jobs_cond;
pthread_mutex_t jobs_mutex; int thread_count;
int count;
pthread_mutex_t count_mutex;
}; typedef struct NMANAGER nThreadPool; // static this file is valid
static void *nThreadCallback(void *arg) {
struct NWORKER *worker = (struct NWORKER*) arg;
while(1) {
pthread_mutex_lock(&worker->pool->jobs_mutex);
while(worker->pool->jobs == NULL) {
if(worker->terminate) break;
// condition wait
pthread_cond_wait(&worker->pool->jobs_cond, &worker->pool->jobs_mutex);
}
if(worker->terminate){
pthread_mutex_unlock(&worker->pool->jobs_mutex);
break;
}
struct NJOB *job = worker->pool->jobs;
LL_REMOVE(job, worker->pool->jobs);
pthread_mutex_unlock(&worker->pool->jobs_mutex);
job->func((NJOB *)job);
pthread_mutex_lock(&worker->pool->count_mutex);
worker->pool->count++;
pthread_mutex_unlock(&worker->pool->count_mutex);
}
free(worker);
pthread_exit(NULL);
} // Thread Pool Create
int nThreadPoolCreate(nThreadPool *pool, int numWorkers) {
if(numWorkers < 1) numWorkers = 1;
if(pool == NULL) return -1;
memset(pool, 0, sizeof(nThreadPool));
pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
memcpy(&pool->jobs_cond, &blank_cond, sizeof(pthread_cond_t));
pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
memcpy(&pool->jobs_mutex, &blank_mutex, sizeof(pthread_mutex_t));
memcpy(&pool->count_mutex, &blank_mutex, sizeof(pthread_mutex_t));
pool->count = 0;
int i = 0;
for(i = 0; i<numWorkers; i++){
struct NWORKER *worker = (struct NWORKER*)malloc(sizeof(struct NWORKER));
if(worker == NULL) {
perror("malloc");
return -2;
}
memset(worker, 0, sizeof(struct NWORKER));
worker->pool = pool; int ret = pthread_create(&worker->thread, NULL, nThreadCallback, worker);
if(ret){
perror("pthread_create");
free(worker);
return -3;
}
LL_ADD(worker, pool->workers);
}
return 0;
} // push job to pool
void nThreadPoolPush(nThreadPool *pool, struct NJOB *job) {
pthread_mutex_lock(&pool->jobs_mutex); LL_ADD(job, pool->jobs);
pthread_cond_signal(&pool->jobs_cond); pthread_mutex_unlock(&pool->jobs_mutex);
} // destroy pool
int nThreadPoolDestroy(nThreadPool *pool){
struct NWORKER *worker = NULL;
for(worker = pool->workers; worker != NULL; worker = worker->next) {
worker->terminate = 1;
}
pthread_mutex_lock(&pool->jobs_mutex);
pthread_cond_broadcast(&pool->jobs_cond);
pthread_mutex_unlock(&pool->jobs_mutex);
return 0;
} #if 1
// 0 --> 1000,
// task -->
void print(struct NJOB *job) {
printf("**%d**\n", *(int *)(job->user_data));
for(int i = 0; i<10000; i++)
for(int j = 0; j < 10000; j++);
}
int main() {
Timer<> timer;
timer.beginStage("START \n"); nThreadPool *pool = new nThreadPool;
nThreadPoolCreate(pool, 16); // create 16 waiter
const int M = 100;
pool->thread_count = M;
NJOB t[M];
int num[M];
for(int i=0; i<M; i++){
num[i] = i;
}
for(int i=0; i < M; i++){
t[i].func = print;
t[i].user_data = &num[i];
nThreadPoolPush(pool, &t[i]);
}
// wait all worker finish
bool check = false;
while(1){
pthread_mutex_lock(&pool->count_mutex);
if(pool->count == pool->thread_count){
check = true;
}
pthread_mutex_unlock(&pool->count_mutex);
unsigned int microseconds = 1000;
usleep(microseconds);
if(check){
nThreadPoolDestroy(pool);
break;
}
}
timer.endStage("END \n");
printf("\n======\n");
} #endif

timee.hh 计时函数

#pragma once
#include <chrono>
#include <iostream>
using namespace std;
template <typename TimeT = std::chrono::milliseconds>
class Timer{
public:
Timer() {
start = std::chrono::system_clock::now();
} size_t value() const {
auto now = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<TimeT>(now - start);
return (size_t) duration.count();
} size_t reset() {
auto now = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<TimeT>(now - start);
start = now;
return (size_t) duration.count();
} void beginStage(const std::string &name){
reset();
std::cout << name << " .. ";
std::cout.flush();
} void endStage(const std::string &str = ""){
std::cout << "done. (took " << value() << " ms";
if(!str.empty()){
std::cout << ", " << str;
}
std::cout << ")" << std::endl;
}
private:
std::chrono::system_clock::time_point start;
};

基于查询法毕竟不太美观

#include <pthread.h>
#include <stdio.h>
#include<string.h>
#include <stdlib.h>
#include "timee.hh"
// Head insert
#define LL_ADD(node, head) do { \
node->prev = NULL; \
node->next = head; \
if(head != NULL) \
head->prev = node; \
head = node; \
} while(0) #define LL_REMOVE(node, head) do { \
if(node->prev != NULL) node->prev->next = node->next; \
if(node->next != NULL) node->next->prev = node->prev; \
if(head == node) head = node->next; \
node->prev = node->next = NULL; \
} while(0) // 线程列表
struct NWORKER
{
pthread_t thread;
struct NMANAGER *pool;
int terminate;
struct NWORKER *prev;
struct NWORKER *next;
}; // 任务列表
struct NJOB
{
void (*func)(struct NJOB *job);
void *user_data;
struct NJOB *prev;
struct NJOB *next;
}; // 管理器
struct NMANAGER
{
struct NWORKER *workers;
struct NJOB *jobs; unsigned int total_jobs; unsigned int job_count; // 任务计数变量
pthread_mutex_t count_mutex; pthread_cond_t end_cond;
pthread_mutex_t end_mutex; pthread_cond_t jobs_cond;
pthread_mutex_t jobs_mutex; // 任何一个线程在干活之前都需要先获取锁
}; typedef struct NMANAGER nThreadPool; // 定义线程所做的工作
static void *nThreadCallback(void *arg)
{
struct NWORKER *worker = (struct NWORKER*) arg;
while(1) {
pthread_mutex_lock(&worker->pool->jobs_mutex); // 干活之前先获取锁
while(worker->pool->jobs == NULL) { // 没有任务
if(worker->terminate) break;
// condition wait
pthread_cond_wait(&worker->pool->jobs_cond, &worker->pool->jobs_mutex);
}
if(worker->terminate){
pthread_mutex_unlock(&worker->pool->jobs_mutex);
break;
}
// 从任务列表获取一个任务进行处理
struct NJOB *job = worker->pool->jobs;
LL_REMOVE(job, worker->pool->jobs);
pthread_mutex_unlock(&worker->pool->jobs_mutex);
job->func(job); pthread_mutex_lock(&worker->pool->count_mutex);
worker->pool->job_count++;
if (worker->pool->job_count == worker->pool->total_jobs) {
pthread_cond_signal(&worker->pool->end_cond);
}
pthread_mutex_unlock(&worker->pool->count_mutex); }
free(worker);
pthread_exit(NULL);
} // Thread Pool Create
int nThreadPoolCreate(nThreadPool *pool, int numWorkers)
{
if(numWorkers < 1) numWorkers = 1;
if(pool == NULL) return -1; memset(pool, 0, sizeof(nThreadPool)); pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
memcpy(&pool->jobs_cond, &blank_cond, sizeof(pthread_cond_t));
memcpy(&pool->end_cond, &blank_cond, sizeof(pthread_cond_t)); pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
memcpy(&pool->jobs_mutex, &blank_mutex, sizeof(pthread_mutex_t));
memcpy(&pool->count_mutex, &blank_mutex, sizeof(pthread_mutex_t));
memcpy(&pool->end_mutex, &blank_mutex, sizeof(pthread_mutex_t)); for(int i = 0; i<numWorkers; i++) {
struct NWORKER *worker = (struct NWORKER*)malloc(sizeof(struct NWORKER)); // 创建一个线程
if(worker == NULL) {
perror("malloc");
return -2;
}
memset(worker, 0, sizeof(struct NWORKER));
worker->pool = pool; // 设置管理器 int ret = pthread_create(&worker->thread, NULL, nThreadCallback, worker);
if(ret){
perror("pthread_create");
free(worker);
return -3;
}
LL_ADD(worker, pool->workers); // 加入线程列表
}
return 0;
} // push job to pool
void nThreadPoolPush(nThreadPool *pool, struct NJOB *job)
{
pthread_mutex_lock(&pool->jobs_mutex); LL_ADD(job, pool->jobs); // 新任务加入任务列表 pthread_cond_signal(&pool->jobs_cond); // 唤醒一个线程去处理 pthread_mutex_unlock(&pool->jobs_mutex); // 释放锁
} // destroy pool
int nThreadPoolDestroy(nThreadPool *pool)
{
struct NWORKER *worker = NULL;
for(worker = pool->workers; worker != NULL; worker = worker->next) {
worker->terminate = 1;
}
pthread_mutex_lock(&pool->jobs_mutex);
pthread_cond_broadcast(&pool->jobs_cond);
pthread_mutex_unlock(&pool->jobs_mutex);
return 0;
} #if 1 void print(struct NJOB *job)
{
printf("**%d**\n", *((int*)job->user_data));
for (int i = 0; i < 10000; i++)
for (int j = 0; j < 10000; j++);
} int main()
{ Timer<> timer;
timer.beginStage("START \n");
nThreadPool *pool = (nThreadPool *)malloc(sizeof(nThreadPool));
nThreadPoolCreate(pool, 16); // create 16 worker #define JOB_COUNT 100 NJOB t[JOB_COUNT];
pool->total_jobs = JOB_COUNT;
pool->job_count = 0; for(int i = 0; i < JOB_COUNT; i++) {
t[i].func = print;
t[i].user_data = (int *)malloc(sizeof(int));
(*(int*)t[i].user_data) = i;
nThreadPoolPush(pool, &t[i]);
} if(pool->job_count != JOB_COUNT) {
pthread_cond_wait(&pool->end_cond, &pool->end_mutex);
printf("==>%d\n", pool->job_count);
nThreadPoolDestroy(pool);
}
timer.endStage("END \n");
} #endif

image



可以看到16个核心跑满了。

相当于一个资本家,对于自己手下的员工,让他不停歇的为你赚钱钱,开心~~

小节

C++的封装对于线程池的实现会更加优雅,但是线程池的C的时间更加粗糙,容易理解。

学了一天,发现,抄一样东西简单,让一样东西实用,理解他,有点点难,或许说我太菜了~~

编译命令

g++ threadpool.cc -lphtread

C++线程池 基于C的实现 学习1的更多相关文章

  1. Java 线程池的原理与实现学习(二)

    java类库中提供的线程池简介: java提供的线程池更加强大,相信理解线程池的工作原理,看类库中的线程池就不会感到陌生了. execute(Runnable command):履行Ruannable ...

  2. C# 多线程的自动管理(线程池) 基于Task的方式

    C# 多线程的自动管理(线程池) 在多线程的程序中,经常会出现两种情况:    1. 应用程序中线程把大部分的时间花费在等待状态,等待某个事件发生,然后给予响应.这一般使用 ThreadPool(线程 ...

  3. Java 线程池的原理与实现学习(三)

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

  4. Java 线程池的原理与实现学习(一)

    线程池:多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.    假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中 ...

  5. .NET 线程池编程技术

    摘要 深度探索 Microsoft .NET提供的线程池, 揭示什么情况下你需要用线程池以及 .NET框架下的线程池是如何实现的,并告诉你如何去使用线程池. 内容 介绍 .NET中的线程池 线程池中执 ...

  6. Android线程管理之ExecutorService线程池

    前言: 上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Execu ...

  7. Linux下简易线程池

    线程池简介 线程池是可以用来在后台执行多个任务的线程集合. 这使主线程可以自由地异步执行其他任务.线程池通常用于服务器应用程序. 每个传入请求都将分配给线程池中的一个线程,因此可以异步处理请求,而不会 ...

  8. Java并发包中线程池的种类和特点介绍

    Java并发包提供了包括原子量.并发集合.同步器.可重入锁.线程池等强大工具这里学习一下线程池的种类和特性介绍. 如果每项任务都分配一个线程,当任务特别多的时候,可能会超出系统承载能力.而且线程的创建 ...

  9. Java调度线程池ScheduleExecutorService(续)

    链接 Java线程池详解(一) Java线程池详解(二) Java调度线程池ScheduleExecutorService 上面列出了最近写的关于java线程池ScheduleExecutorServ ...

  10. Java并发包——线程池

    Java并发包——线程池 摘要:本文主要学习了Java并发包中的线程池. 部分内容来自以下博客: https://www.cnblogs.com/dolphin0520/p/3932921.html ...

随机推荐

  1. python之“if __name__=="__main__"”的代表的意思和用法

    创建下方脚本A def print_sum(a): print(a) print_sum(20) if __name__=="__main__": print("test ...

  2. 一、Java语言介绍

    1.硬件知识介绍 2.常用dos命令以及快捷键 1 /** 2 *@desc: 复习 3 *@Description: 4 * dir:列出当前文件目录下的所有文件; 5 * md:创建一个新目录; ...

  3. CF1648A题解

    题意: 给定 n×mn\times mn×m 的矩阵,求相同的数的曼哈顿距离和. 思路: 曼哈顿距离:disi→j=∣xj−xi∣+∣yj−yi∣dis_{i\to j}=|x_j - x_i| + ...

  4. EFCore(五)——多个DBContext的Code First指定对应的DBContext更新

    此环境为ASP.NET Core的项目 1.在需要更新的DBContext里添加空的构造函数 2.打开Nuget命令行选择对应的目录位置 3.带参数-Context指定对应的DBContext 1.  ...

  5. 关于navicat导出和导入sql文件的方法

    导出SQL文件 导入SQL文件 导出技巧 导出SQL文件 到处数据库的方法很简单,只需要在要到处的数据库上面右键,选择转储SQL文件,可以选结构和数据...或者结构... 导入SQL文件 导如SQL文 ...

  6. 【翻译】 Processing系列|(三)安卓项目构建

    上上篇:[翻译]Processing系列|(一)简介及使用方法 上一篇:[翻译]Processing系列|(二)安卓模式的安装使用及打包发布 我顺藤摸瓜找到了Github仓库,然后发现人家主要还是用A ...

  7. 【HUST】网络攻防实践|5_二进制文件补丁技术|实验三 热补丁

    文章目录 实验要求 实验过程 1. 64位Ubuntu下先安装32位库 2. 利用Preload Hook实现热补丁修补 3. 利用系统调用`ptrace`对运行状态的程序进行hook 3.1 编写补 ...

  8. 【MOOC】华中科技大学操作系统慕课答案-第4~6章+第7章单元测试

    文章目录 单选 填空 判断 第七章答案 单选 1 ‎关于进程错误的说法是 . A. 进程的运行全过程不可重现. √B. 一个程序只能生成一个进程. C. 进程具有异步性. D. 多个并发进程共享CPU ...

  9. Axure通用电商后台管理系高保真交互模板原型图附元件库4种后台模板风格

    Axure通用电商后台管理交互模板原型图附元件库4种后台模板风格,原型中使用4种不同的布局框架,你可以根据自己的需求,去选中对应的菜单排版布局.另外,原型图中使用了较多的交互元件.母版.动态面板,基本 ...

  10. django实例(4):一对多外键关联

    程序目录 Project-->urls.pyfrom django.contrib import adminfrom django.conf.urls import url,includeurl ...