C++实现一个通用线程池

C++实现一个通用线程池

简介

本文主要记录一个自己实现的简单的通用线程池对象。主要包含以下几个类:

  • Platforms.h: 屏蔽平台差异,提供跨平台使用的函数, 有需要可以未来进行扩展
  • SingleTon.h: 线程安全的单例类
  • ThreadPool.h: 线程池的实现
  • main.cpp: 测试代码

Platforms.h

#pragma once

#ifdef WIN32
#include <Windows.h>
#endif // WIN32

class Platforms
{
public:
	unsigned long GetCurrentThreadId()
	{
		unsigned long id = 0;
#ifdef WIN32
		id = ::GetCurrentThreadId();
#endif // WIN32
		return id;
	}
};

SingleTon.h

#pragma once

/*
* Date: 2022-06-21
* Author: nicklaus
*/
template<typename T>
class SingleTon {
private:
	SingleTon() {}
public:
	/*
	* 使用单例包装并返回原生类型T
	*/
	static T* GetInstance()
	{
		static T instance;
		return &instance;
	}
};

ThreadPool.h

#pragma once

#include<memory>
#include<functional>
#include<thread>
#include<mutex>
#include<condition_variable>

#include<queue>
#include<vector>

#include<stdio.h>
#include"SingleTon.h"
#include"Platforms.h"

class ThreadPool
{
public:
	using Task = std::function<void()>;

	ThreadPool(int threadNum) 
	{
		createThreads(threadNum);
	}
	~ThreadPool() 
	{
		Stop();
	}

	ThreadPool(const ThreadPool& pool) = delete;
	ThreadPool& operator=(const ThreadPool& pool) = delete;
	
	void AddTask(const Task& task)
	{
		std::lock_guard<std::mutex> locker(m_taskQueueMutex);
		m_taskQueue.push(task);

		m_notEmpty.notify_one();
	}

	void AddTask(Task&& task)
	{
		std::lock_guard<std::mutex> locker(m_taskQueueMutex);
		m_taskQueue.push(std::forward<Task>(task));

		m_notEmpty.notify_one();
	}

private:
	void createThreads(int num)
	{
		for (auto i = 0; i < num; i++)
		{
			m_threadGroup.push_back(std::make_shared<std::thread>(&ThreadPool::threadproc, this));
		}
	}

	void Stop()
	{
		m_bForceStoped = true;

		m_notEmpty.notify_all();

		for (auto i = 0; i < m_threadGroup.size(); i++)
		{
			if (m_threadGroup[i] != nullptr)
			{
				m_threadGroup[i]->join();
			}
		}

		m_threadGroup.clear();
	}

	bool IsForceStoped()
	{
		return m_bForceStoped;
	}

	bool IsTaskQueueEmpty()
	{
		return m_taskQueue.empty();
	}

	void threadproc()
	{
		while (true)
		{
			if (!IsForceStoped())// 正常情况,线程池还处于运行状态,池中的线程们需要互斥取任务执行
			{
				std::unique_lock<std::mutex> locker(m_taskQueueMutex);
				// 如果是按下面这行代码不检测,是否强制停止会有下面这种现象:
				// 主线程通过AddTask向m_taskQueue中添加任务,之后通过OS的调度,使得线程池中的某一个线程取到所有任务,并执行,
				// 此时外界不再添加任何任务进来,那么如果按照下面的写法,那么线程中的所有子线程会阻塞在wait函数处,导致ThreadPool
				// 的析构永远不能终结,所以wait的等待条件需要加上IsForceStoped()来避免这种情况
				//m_notEmpty.wait(locker, [this] {return !IsTaskQueueEmpty(); });
				m_notEmpty.wait(locker, [this] {return IsForceStoped() || !IsTaskQueueEmpty(); });

				std::queue<Task> tasks = std::move(m_taskQueue);
				while (!tasks.empty())
				{
					auto task = tasks.front();
					tasks.pop();
					printf("thread[%d] run task: %s\n",
						SingleTon<Platforms>::GetInstance()->GetCurrentThreadId(),
						typeid(task).name());
					task();
				}
			}
			else // 强制停止线程池运行,需要保证任务队列中的任务能够运行结束
			{
				std::unique_lock<std::mutex> locker(m_taskQueueMutex);
				if (!IsTaskQueueEmpty())
				{
					std::queue<Task> tasks = std::move(m_taskQueue);
					while (!tasks.empty())
					{
						auto task = tasks.front();
						tasks.pop();
						task();
					}
				}

				printf("打印次数如果等于线程数量说明没有无限循环\n"); // 可以取消注释看看
				break; // 必须break,不然会导致无限循环(都强制停止了,必须结束线程体的运行)
			}
		}
	}

private:
	std::mutex	m_taskQueueMutex;
	std::queue<Task> m_taskQueue;
	std::condition_variable	m_notEmpty;

	bool m_bForceStoped;					// 设计初衷:线程池中的线程不允许写,只允许读(特别重要)
	std::vector<std::shared_ptr<std::thread>> m_threadGroup;
};

main.cpp

#include"ThreadPool.h"
#include"SingleTon.h"
#include"Platforms.h"

void printA_Z()
{
	printf("%s: ", __FUNCTION__);
	for (char c = 'A'; c <= 'Z'; c++)
	{
		printf("%c ", c);
	}
	printf("\n\n");
}

void print1_100()
{
	printf("%s: ", __FUNCTION__);
	for (int c = 1; c <= 100; c++)
	{
		printf("%d ", c);
	}
	printf("\n\n");
}

class A 
{
public:
	void f() 
	{
		printf("%s: ", __FUNCTION__);
		for (int c = 1; c <= 100; c++)
		{
			printf("%d ", c);
		}
		printf("\n\n");
	}
};


int main()
{
	printf("main thread id:%d\n", SingleTon<Platforms>::GetInstance()->GetCurrentThreadId());

	{
		A a;

		ThreadPool pool(10);
		pool.AddTask(printA_Z);
		pool.AddTask(print1_100);
		pool.AddTask(printA_Z);
		pool.AddTask(print1_100);
		pool.AddTask(printA_Z);
		pool.AddTask(print1_100);
		pool.AddTask(std::bind(&A::f, a));
		Sleep(1000);
	}

	printf("main thread not block\n");
	return 0;
}

测试结果

main thread id:236676
thread[232416] run task: class std::function<void __cdecl(void)>
printA_Z: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

thread[232416] run task: class std::function<void __cdecl(void)>
print1_100: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

thread[232416] run task: class std::function<void __cdecl(void)>
printA_Z: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

thread[232416] run task: class std::function<void __cdecl(void)>
print1_100: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

thread[232416] run task: class std::function<void __cdecl(void)>
printA_Z: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

thread[232416] run task: class std::function<void __cdecl(void)>
print1_100: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

thread[232416] run task: class std::function<void __cdecl(void)>
A::f: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

打印次数如果等于线程数量说明没有无限循环
打印次数如果等于线程数量说明没有无限循环
打印次数如果等于线程数量说明没有无限循环
打印次数如果等于线程数量说明没有无限循环
打印次数如果等于线程数量说明没有无限循环
打印次数如果等于线程数量说明没有无限循环
打印次数如果等于线程数量说明没有无限循环
打印次数如果等于线程数量说明没有无限循环
打印次数如果等于线程数量说明没有无限循环
打印次数如果等于线程数量说明没有无限循环
main thread not block

F:\HappyCoding\threadpool\Debug\threadpool.exe (process 236836) exited with code 0.
Press any key to close this window . . .
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用C语言实现一个简单的线程池的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #define MAX_THREADS 10 #define MAX_QUEUE 100 typedef struct { void (*function)(void *); void *argument; } task_t; typedef struct { task_t buffer[MAX_QUEUE]; pthread_mutex_t lock; int read_pos, write_pos; pthread_cond_t not_empty; pthread_cond_t not_full; } task_queue_t; typedef struct { pthread_t threads[MAX_THREADS]; int thread_count; task_queue_t queue; } thread_pool_t; void task_queue_init(task_queue_t *queue) { pthread_mutex_init(&queue->lock, NULL); pthread_cond_init(&queue->not_empty, NULL); pthread_cond_init(&queue->not_full, NULL); queue->read_pos = 0; queue->write_pos = 0; } void task_queue_destroy(task_queue_t *queue) { pthread_mutex_destroy(&queue->lock); pthread_cond_destroy(&queue->not_empty); pthread_cond_destroy(&queue->not_full); } void task_queue_put(task_queue_t *queue, task_t task) { pthread_mutex_lock(&queue->lock); while ((queue->write_pos + 1) % MAX_QUEUE == queue->read_pos) { pthread_cond_wait(&queue->not_full, &queue->lock); } queue->buffer[queue->write_pos] = task; queue->write_pos = (queue->write_pos + 1) % MAX_QUEUE; pthread_cond_signal(&queue->not_empty); pthread_mutex_unlock(&queue->lock); } task_t task_queue_get(task_queue_t *queue) { task_t task; pthread_mutex_lock(&queue->lock); while (queue->write_pos == queue->read_pos) { pthread_cond_wait(&queue->not_empty, &queue->lock); } task = queue->buffer[queue->read_pos]; queue->read_pos = (queue->read_pos + 1) % MAX_QUEUE; pthread_cond_signal(&queue->not_full); pthread_mutex_unlock(&queue->lock); return task; } void *worker_thread(void *arg) { thread_pool_t *pool = (thread_pool_t *)arg; while (1) { task_t task = task_queue_get(&pool->queue); void (*function)(void *) = task.function; void *argument = task.argument; function(argument); } return NULL; } void thread_pool_init(thread_pool_t *pool) { int i; pool->thread_count = 0; task_queue_init(&pool->queue); for (i = 0; i < MAX_THREADS; i++) { pthread_create(&pool->threads[i], NULL, worker_thread, pool); pool->thread_count++; } } void thread_pool_submit(thread_pool_t *pool, void (*function)(void *), void *argument) { task_t task = { function, argument }; task_queue_put(&pool->queue, task); } void thread_pool_shutdown(thread_pool_t *pool) { int i; for (i = 0; i < pool->thread_count; i++) { pthread_cancel(pool->threads[i]); } task_queue_destroy(&pool->queue); } void *print_message(void *arg) { char *message = (char *)arg; printf("%s\n", message); return NULL; } int main() { thread_pool_t pool; thread_pool_init(&pool); thread_pool_submit(&pool, print_message, "Hello, world!"); thread_pool_submit(&pool, print_message, "Goodbye, world!"); getchar(); thread_pool_shutdown(&pool); return 0; } ``` 该示例程序包含了以下几个核心组件: - `task_t` 结构体:用于存储任务函数和参数。 - `task_queue_t` 结构体:用于实现任务队列,支持多线程读操作和阻塞等待。 - `thread_pool_t` 结构体:用于管理线程池,包括线程数组和任务队列。 - `task_queue_init` 和 `task_queue_destroy` 函数:用于初始化和销毁任务队列。 - `task_queue_put` 和 `task_queue_get` 函数:用于添加和获取任务,支持阻塞等待。 - `worker_thread` 函数:用于实现工作线程的逻辑,不断从任务队列中读取任务并执行。 - `thread_pool_init`、`thread_pool_submit` 和 `thread_pool_shutdown` 函数:用于初始化、提交和销毁线程池。 - `print_message` 函数:用于测试线程池,输出一段文本。 在主函数中,我们首先初始化线程池,然后向任务队列中提交两个任务函数,最后等待用户按下 Enter 键,然后销毁线程池。当线程池被销毁时,所有工作线程都会被取消,并且任务队列也会被销毁。 需要注意的是,在实际使用中,我们可能需要添加更多的功能,例如限制任务队列的最大长度,限制线程池的最大线程数等等。此外,在实际使用中,我们还需要确保任务函数的线程安全性,并且避免出现死锁等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值