[代码实例][C]Linux实现线程池

2023-05-16

ThreadPool.h

#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <stdbool.h>

#include <pthread.h>

struct ThreadJob
{
	void* (*CallbackFunction)(void *arg);	// 线程回调函数
	void * Arg;								// 回调函数参数
	struct ThreadJob * Next;
};

typedef struct
{
	int ThreadCount;					// 线程池中开启线程的个数
	int QueueMaxCount;					// 队列中最大ThreadJob的个数
	struct ThreadJob * JobQueueHead;	// 指向ThreadJob的头指针
	struct ThreadJob * JobQueueTail;	// 指向ThreadJob的尾指针
	pthread_t * Threads;				// 线程池中所有线程的pthread_t
	pthread_mutex_t Mutex;				// 互斥信号量
	pthread_cond_t QueueEmptyCond;		// 队列为空的条件变量
	pthread_cond_t QueueNotEmptyCond;	// 队列不为空的条件变量
	pthread_cond_t QueueNotFullCond;	// 队列不为满的条件变量
	int QueueCount;						// 队列当前的ThreadJob个数
	bool IsQueueClosed;					// 队列是否已经关闭
	bool IsPoolClosed;					// 线程池是否已经关闭
} ThreadPool;

/*
 * Function: ThreadPool_Init
 * Description: 初始化线程池。
 * Arguments:
 *	@threadCount: 线程池大小。
 *	@queueMaxCount: 线程池队列大小。
 * Return: 指向线程池的指针。
 */
ThreadPool * ThreadPool_Init(int threadCount, int queueMaxCount);

/*
 * Function: ThreadPool_AddJob
 * Description: 向线程池添加任务。
 * Arguments:
 *	@threadPool: 指向线程池的指针。
 *	@callbackFunction: 线程回调函数。
 *	@arg: 回调函数参数。
 * Return: null。
 */
void ThreadPool_AddJob(ThreadPool * threadPool, void * (*callbackFunction)(void * arg), void * arg);

/*
 * Function: ThreadPool_Destroy
 * Description: 销毁线程池。
 * Arguments:
 *	@threadPool: 指向线程池的指针。
 * Return: null。
 */
void ThreadPool_Destroy(ThreadPool * threadPool);

#endif

ThreadPool.c

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

#include <stdbool.h>

#include "ThreadPool.h"

static void * _function(void * arg)
{
	if(!arg)
	{
		fprintf(stderr, "[_function()] Argument 'arg' is invalid");
		exit(EXIT_FAILURE);
	}

	ThreadPool * threadPool = (ThreadPool *)arg;

	struct ThreadJob *threadJob = NULL;
	while (true)
	{
		pthread_mutex_lock(&(threadPool->Mutex));

		//队列为空时,就等待队列非空
		while (threadPool->QueueCount == 0 && !threadPool->IsPoolClosed)
			pthread_cond_wait(&(threadPool->QueueNotEmptyCond), &(threadPool->Mutex));
		
		//线程池关闭
		if (threadPool->IsPoolClosed)
		{
			pthread_mutex_unlock(&(threadPool->Mutex));
			pthread_exit(NULL);
		}

		threadPool->QueueCount--;
		threadJob = threadPool->JobQueueHead;
		if (threadPool->QueueCount == 0)
		{
			threadPool->JobQueueHead = NULL;
			threadPool->JobQueueTail = NULL;
		}
		else
			threadPool->JobQueueHead = threadJob->Next;

		// 若队列为空,就可以通知ThreadPool_Destroy函数,销毁线程函数
		if (threadPool->QueueCount == 0)
			pthread_cond_signal(&(threadPool->QueueEmptyCond));
		
		// 队列非满,就可以通知ThreadPool_AddJob函数,添加新任务
		if (threadPool->QueueCount == threadPool->QueueMaxCount - 1)
			pthread_cond_broadcast(&(threadPool->QueueNotFullCond));  
		
		pthread_mutex_unlock(&(threadPool->Mutex));
		
		// 线程真正要做的任务,回调函数的调用
		(*(threadJob->CallbackFunction))(threadJob->Arg);
		
		free(threadJob);
	}
}

ThreadPool * ThreadPool_Init(int threadCount, int queueMaxCount)
{
	ThreadPool * threadPool = NULL;

	threadPool = malloc(sizeof(ThreadPool));
	if (!threadPool)
	{
		perror("[ThreadPool_Init()] malloc");
		exit(EXIT_FAILURE);
	}

	threadPool->ThreadCount = threadCount;
	threadPool->QueueMaxCount = queueMaxCount;
	threadPool->QueueCount = 0;
	threadPool->JobQueueHead = NULL;
	threadPool->JobQueueTail = NULL;
		
	if (pthread_mutex_init(&(threadPool->Mutex), NULL))
	{
		perror("[ThreadPool_Init()] pthread_mutex_init");
		exit(EXIT_FAILURE);
	}
	if (pthread_cond_init(&(threadPool->QueueEmptyCond), NULL))
	{
		perror("[ThreadPool_Init()] pthread_mutex_init");
		exit(EXIT_FAILURE);
	}
	if (pthread_cond_init(&(threadPool->QueueNotEmptyCond), NULL))
	{
		perror("[ThreadPool_Init()] pthread_cond_init");
		exit(EXIT_FAILURE);
	}
	if (pthread_cond_init(&(threadPool->QueueNotFullCond), NULL))
	{
		perror("[ThreadPool_Init()] pthread_cond_init");
		exit(EXIT_FAILURE);
	}
		
	threadPool->Threads = malloc(sizeof(pthread_t) * threadCount);
	if (!threadPool->Threads)
	{
		perror("[ThreadPool_Init()] malloc");
		exit(EXIT_FAILURE);
	}

	threadPool->IsQueueClosed = false;
	threadPool->IsPoolClosed = false;
	for (int i = 0; i < threadPool->ThreadCount; ++i)
		pthread_create(&(threadPool->Threads[i]), NULL, _function, threadPool);
		
	return threadPool;
}

void ThreadPool_AddJob(ThreadPool * threadPool, void * (*callbackFunction)(void * arg), void * arg)
{
	if(!threadPool)
	{
		fprintf(stderr, "[ThreadPool_AddJob()] Argument 'threadPool' is invalid");
		exit(EXIT_FAILURE);
	}
	if(!callbackFunction)
	{
		fprintf(stderr, "[ThreadPool_AddJob()] Argument 'callbackFunction' is invalid");
		exit(EXIT_FAILURE);
	}

	pthread_mutex_lock(&(threadPool->Mutex));

	// 队列满的时候就等待
	while (threadPool->QueueCount == threadPool->QueueMaxCount
			&& !(threadPool->IsQueueClosed || threadPool->IsPoolClosed))
		pthread_cond_wait(&(threadPool->QueueNotFullCond), &(threadPool->Mutex));
	
	// 队列关闭或者线程池关闭就退出
	if (threadPool->IsQueueClosed || threadPool->IsPoolClosed)
	{
		fprintf(stderr, "[ThreadPool_AddJob()] ThreadPool or Queue has been closed");
		exit(EXIT_FAILURE);
	}

	struct ThreadJob * threadJob = malloc(sizeof(struct ThreadJob));
	if (!threadJob)
	{
		perror("[ThreadPool_AddJob()] malloc");
		exit(EXIT_FAILURE);
	}

	threadJob->CallbackFunction = callbackFunction;    
	threadJob->Arg = arg;
	threadJob->Next = NULL;
	
	if (threadPool->JobQueueHead == NULL)   
	{
		threadPool->JobQueueHead = threadJob;
		threadPool->JobQueueTail = threadJob;

		// 队列空的时候,有任务来时就通知线程池中的线程:队列非空
		pthread_cond_broadcast(&(threadPool->QueueNotEmptyCond));
	}
	else
	{
		threadPool->JobQueueTail->Next = threadJob;
		threadPool->JobQueueTail = threadJob;    
	}

	threadPool->QueueCount++;

	pthread_mutex_unlock(&(threadPool->Mutex));
}

void ThreadPool_Destroy(ThreadPool * threadPool)
{
	if(!threadPool)
	{
		fprintf(stderr, "[ThreadPool_AddJob()] Argument 'threadPool' is invalid");
		exit(EXIT_FAILURE);
	}

	pthread_mutex_lock(&(threadPool->Mutex));

	// 若线程池或队列已经关闭,则说明已发生内部错误
	if (threadPool->IsQueueClosed || threadPool->IsPoolClosed)
	{
		fprintf(stderr, "[ThreadPool_Destroy()] ThreadPool or Queue has been closed");
		exit(EXIT_FAILURE);
	}
	
	// 设置队列关闭
	threadPool->IsQueueClosed = true;
	// 等待队列为空
	while (threadPool->QueueCount != 0)
		pthread_cond_wait(&(threadPool->QueueEmptyCond), &(threadPool->Mutex));
	
	// 设置线程池关闭
	threadPool->IsPoolClosed = true;

	pthread_mutex_unlock(&(threadPool->Mutex));

	// 唤醒线程池中正在阻塞的线程
	pthread_cond_broadcast(&(threadPool->QueueNotEmptyCond));
	//唤醒添加任务的ThreadPool_AddJob函数
	pthread_cond_broadcast(&(threadPool->QueueNotFullCond));
	
	// 等待线程池的所有线程执行完毕
	for (int i = 0; i < threadPool->ThreadCount; ++i)
		pthread_join(threadPool->Threads[i], NULL);
	
	/* 清理相关资源 */
	pthread_mutex_destroy(&(threadPool->Mutex));
	pthread_cond_destroy(&(threadPool->QueueEmptyCond));
	pthread_cond_destroy(&(threadPool->QueueNotEmptyCond));
	pthread_cond_destroy(&(threadPool->QueueNotFullCond));
	
	free(threadPool->Threads);

	struct ThreadJob * p = NULL;
	while (threadPool->JobQueueHead != NULL)
	{
		p = threadPool->JobQueueHead;
		threadPool->JobQueueHead = p->Next;
		free(p);
	}

	free(threadPool);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[代码实例][C]Linux实现线程池 的相关文章

  • C#中Tuple的使用

    定义 xff1a 元组是具有 特定数量和序列 的元素 的数据结构 xff08 注意断句哈 xff01 xff09 元组通常有四种使用方式 一 表示一组数据 例如 xff0c 一个元组可以表示一条数据库记录 xff0c 并且每一个分量对应表示
  • 调用C++dll

    1 dll 的优点 代码复用是提高软件开发效率的重要途径 一般而言 xff0c 只要某部分代码具有通用性 xff0c 就可将它构造成相对独立的功能模块并在之后的项目中重复使用 比较常见的例子是各种应用程序框架 xff0c ATL MFC 等
  • DLL程序入口DllMain详解

    DLL程序入口点函数 xff1a DllMain xff0c 注意 xff1a 大小写是区别的 仅导出资源的DLL可以没有DllMain函数 函数原型 cpp view plain copy print BOOL APIENTRY DllM
  • C++编写DLL的方法

    在写C 43 43 程序时 xff0c 时常需要将一个class写成DLL xff0c 供客户端程序调用 这样的DLL可以导出整个class 也可以导出这个class的某个方法 一 导出整个class 方法很简单 xff0c 只需要在类的头
  • dll文件的c++制作

    1 首先用vs2005建立一个 c 43 43 的dll 动态 链接 库 文件 xff0c 这时 xff0c DllTest cpp 定义 DLL 应用程序的入口点 include 34 stdafx h 34 include 34 Dll
  • vs2008 C++创建和调用标准DLL

    为了共享代码 xff0c 需要生成标准的dll xff0c 本文将介绍在vs2008 C 43 43 生成及调用dll 一 生成DLL 生成一个名为FunDll的dll文件 xff0c 对外函数为addl step1 vs2008 环境下
  • VSCode使用SSH免密登录服务器

    VSCode使用SSH免密登录服务器 前言一 SSH连接服务器1 1 安装ssh插件1 2 配置连接 二 免密登录服务器2 1 生成公钥2 2 服务器新建授权文件2 3 本地复制公钥到服务器 总结 前言 vscode使用remote ssh
  • 使用directX 7结合C#进行2D游戏编程

    使用directX 7结合C xff03 进行2D游戏编程 前言 对于C 的开发人员来讲 xff0c GDI 43 是一个拥有丰富的绘图API指令 传统 高效的程序集 但不幸的是 xff0c 你要想用她来开发一个复杂而又平滑的动画的时候 x
  • DirectX学习笔记_关于Sprite.Draw2D的说明

    在DirectX的Sprite中提供一个Draw2D的方法 xff0c 该方法绘制一个 Sprite 对象用于二维空间中显示 xff0c 在DirectX 9 0C中 xff0c 该方法有6个重载 xff0c 分别是 1 public vo
  • 关于Direct2D

    关于Direct2D Direct2D是一个硬件加速的 xff0c 提供立即模式的二维图形API 它提供了二维的几何体 xff0c 位图 xff0c 文本的高性能 xff0c 高质量的渲染 十分方便的是 xff0c Direct2D与GDI
  • C# GDI+ 绘图

    1 坐标系统 1 坐标原点 xff1a 在窗体或控件的左上角 xff0c 坐标为 0 0 2 正方向 xff1a X轴正方向为水平向右 xff0c Y轴正方向为竖直向下 3 单位 xff1a 在设置时 xff0c 一般以像素为单位 xff0
  • lock锁和monitor.enter锁

    210 08 05 14 50 28 转载 Lock object 锁的使用 using System using System Threading namespace program class wangjun public static
  • windows重绘机制原理

    一 Windows程序中的绘制和更新 与DOS环境比较 xff0c Windows中的应用程序在处理文字和图形绘制时有以下区别 xff1a 1 只能在窗口的客户区域绘制文字和图形 2 在窗口上绘制的内容不一定能够保留到程序下一次有意地改写时
  • C# Winform 出现异常:无法将顶级控件添加到控件,解决方案如下:

    Form1Test frm 61 new Form1Test frm TopLevel 61 false 重要的一个步骤 frm Parent 61 splitContainerPanel Panel2 frm Show
  • c#中的Form.Show和Form.ShowDialog的区别

    出处 xff1a http hi baidu com cysteine blog item 01e32224702ff5398744f9bf html 区别1 xff1a ShowDialog是模态的 xff08 独占用户输入 xff09
  • 浅析C#中foreach引用变量

    昨天做老师的网站作业 要对一些对象做添加修改删除处理 别的倒没什么 xff0c 删除时出现了点问题似的 因为是从一个类的集合中删除掉一个元素 这样就要遍历整个集合 xff0c 而foreach正是为遍历准备的新玩意 自然而然用上了 于是代码
  • 用汇编的眼光看C++(之拷贝、赋值函数)

    拷贝构造函数和复制函数是类里面比较重要的两个函数 两者有什么区别呢 xff1f 其实也很简单 xff0c 我们可以举个例子 xff0c 加入有这样一个类的定义 xff1a cpp view plain copy class apple pu
  • IDEA配置一个入门的ssh事例,解决Artifact war exploded:Error during artifact deployment. See server log for detail

    首先 xff0c 我的版本ideaIU 2017 3 4 xff0c 一 新建一个项目project 1 1 create new project 2 file new project 2 这里选择spring xff0c struts2
  • WinForm 之Control.Invoke 和Control.BeginInvoke 方法的使用 Control 不能在创建它的 Thread 之外被调用。但可以通过 invoke 来保证 C

    WinForm 之Control Invoke 和Control BeginInvoke 方法的使用 Control 不能在创建它的 Thread 之外被调用 但可以通过 invoke 来保证 Control 线程安全 在跨线程更新的时候
  • C#中跨线程访问控件问题解决方案

    net 原则上禁止跨线程访问控件 xff0c 因为这样可能造成错误的发生 xff0c 推荐的解决方法是采用代理 用代理方法来间接操作不是同一线程创建的控件 第二种方法是禁止编译器对跨线程访问作检查 xff0c 可以实现访问 xff0c 但是

随机推荐

  • C#中Invoke的用法(转)

    转载 转自 xff1a http blog 3snews net html 30 34530 27563 html 在多线程编程中 xff0c 我们经常要在工作线程中去更新界面显示 xff0c 而在多线程中直接调用界面控件的方法是错误的做法
  • 【分析】浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang)

    今天无意中看到有关Invoke和BeginInvoke的一些资料 xff0c 不太清楚它们之间的区别 所以花了点时间研究了下 据msdn中介绍 xff0c 它们最大的区别就是BeginInvoke属于异步执行的 Control Invoke
  • C#中Invoke 和 BeginInvoke的涵义和区别

    BeginInvoke 方法真的是新开一个线程进行异步调用吗 xff1f 参考以下代码 xff1a public delegate void treeinvoke private void UpdateTreeView MessageBox
  • C# 理解lock

    一 为什么要lock xff0c lock了什么 xff1f 当我们使用 线程 的时候 xff0c 效率最高的方式当然是 异步 xff0c 即各个线程同时运行 xff0c 其间不相互依赖和等待 但当不同的线程都需要访问某个资源的时候 xff
  • excel操作的几种方法

    using System using System Collections Generic using System Text using System Data using System Windows Forms using Syste
  • c#中如何实现拷贝对象

    大家都知道 xff0c 在C 中变量的存储分为值类型和引用类型两种 xff0c 而值类型和引用类型在数值变化是产生的后果是不一样的 xff0c 值类型我们可以轻松实现数值的拷贝 xff0c 那么引用类型呢 xff0c 在对象拷贝上存在着一定
  • 深入了解Windows句柄到底是什么

    总是有新入门的Windows程序员问我Windows的句柄到底是什么 xff0c 我说你把它看做一种类似指针的标识就行了 xff0c 但是显然这一答案不能让他们满意 xff0c 然后我说去问问度娘吧 xff0c 他们说不行网上的说法太多还难
  • 句柄概念

    句柄 xff08 handle xff09 xff0c 有多种意义 xff0c 其中第一种是指程序设计 xff0c 第二种是指Windows编程 现在大部分都是指程序设计 程序开发这类 第一种解释 xff1a 句柄是一种特殊的 智能指针 当
  • 腾讯云大数据发布最新产品矩阵,助力企业整合打通海量数据

    9月11日 xff0c 主题为 释放数字经济发展的新动能 的腾讯全球数字生态大会大数据专场在线上拉开帷幕 腾讯大数据领域的多位顶级专家 xff0c 与包括 Hadoop 创始人 Doug Cutting 在内的业内顶级大咖 xff0c 以及
  • C/C++中函数参数传递

    看了内存管理的有关内容 xff0c 有一点了解 xff0c 但不是很深入 xff0c 发现之前写代码时有很多细节问题没有注意到 xff0c 只知道这样做可以实现功能 xff0c 却不知道为什么可以这样 xff0c 对于采用自己的方法造成的隐
  • Windows窗口刷新机制详解

    1 Windows的窗口刷新管理 窗口句柄 xff08 HWND xff09 都是由操作系统内核管理的 xff0c 系统内部有一个z order序列 xff0c 记录着当前窗口从屏幕底部 xff08 假象的从屏幕到眼睛的方向 xff09 x
  • C语言中内存分布及程序运行中(BSS段、数据段、代码段、堆栈)

    BSS段 xff08 bss segment xff09 通常是指用来存放程序中 未初始化 的 全局变量 的一块内存区域 BSS是英文Block Started by Symbol的简称 BSS段属于静态内存分配 数据段 xff1a 数据段
  • C# 窗体Show和ShowDialog 方法的区别详解

    CenterParent 窗体在其父窗体中居中 CenterScreen 窗体在当前显示窗口中居中 xff0c 其尺寸在窗体大小中指定 Manual 窗体的位置由 Location 属性确定 WindowsDefaultBounds 窗体定
  • c# 窗口句柄问题 。

    1 如何获得一个窗口的句柄 xff1f 例如获取窗口PictureBox控件 xff08 其他控件也可以 xff09 的句柄 xff0c csharp view plain copy IntPtr handle 61 pictureBox
  • c#中已知一个外部窗口的句柄,怎么关闭

    已知一个外部窗口的句柄 xff0c 怎么关闭它 怎么给这个窗口的一个文本框设置内容 public void Test Handle windowhandle string TextBoxName System windws froms fr
  • 数组内存分配概念

    在这里解答一下 xff1a int arr 4 amp arr 1 61 arr 0 43 sizeof int 静态分配 xff0c 即普通数组 xff0c 由于在栈中分配 xff0c 而栈的生成方向是自高地址向低地址生成 所以有 xff
  • 静态数组和动态数组 内存分布

    数组是程序设计中是一个非常重要的概念 数组是一个用于收集大量类似数据的容器 xff0c 以及其每一个元素能被相同处理过程迭代来处理的一个抽象体 创建数组一般有三种方式 xff1a 全局 静态范围的数组 xff0c 局部变量数组 xff0c
  • C语言 内存分配 地址 指针 数组 参数 解析

    指针简介 指针式保存变量地址 的变量 增加阅读难度 指针 和 goto 语句会增加程序的理解难度 容易出现错误 ANSI C American National Standards Institute 美国国家标准学会 即标准C 通用指针类
  • C++数组与指针概念

    指向数组元素的指针 一个变量有地址 xff0c 一个数组包含若干元素 xff0c 每个数组元素都在内存中占用存储单元 xff0c 它们都有相应的地址 指针变量既然可以指向变量 xff0c 当然也可以指向数组元素 xff08 把某一元素的地址
  • [代码实例][C]Linux实现线程池

    ThreadPool h span class token macro property span class token directive keyword ifndef span THREADPOOL H span span class