C++线程与线程池

C++多线程技术与线程池

0: 获取线程ID


#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>

std::uint32_t lwp_id()
{
#if defined(APPLE) || defined(__APPLE__) || defined (__apple__)
	return static_cast<std::uint32_t>(std::this_thread::get_id());
#else
	return static_cast<std::uint32_t>(syscall(SYS_gettid));
#endif
}

1: C++线程技术

C++11提供了对线程技术的支持,

#include <thread>
#include <future>
#include <vector>
#include <iostream>
#include <cmath>
using namespace std;

struct result {
	vector<double> x;
	double y;
};

void f1(promise<result> &res)
{
	vector<double> vec(2);
	vec[0] = 1.5;
	vec[1] = 2.2;
	res.set_value({ vec, 4.7 });
}

double f2(double x)
{
	return sin(x);
}

int main(int argc, char* argv[])
{
	promise<result> res1;
	packaged_task<double(double)> res2(f2);
	future<result> ft1 = res1.get_future();
	future<double> ft2 = res2.get_future();
	thread th1(f1, ref(res1));       // 线程参数均为copy,ref代表引用,必须显式给出
	thread th2(move(res2), 3.14/6);  // 为了提高性能采用move

	ft1.wait();
	ft2.wait();

	result r1 = ft1.get();
	double r2 = ft2.get();

	th1.join();  // 这两句对此例不必要,仅为了逻辑自洽给出
	th2.join();

	cout << "result: x = (" << r1.x[0] << ", " << r1.x[1] << "), y = " << r1.y << endl;
	cout << "sin(pi/6) = " << r2 << endl;
	return 0;
}

2: 线程池

//threadpool.h

#pragma once
#ifndef THREAD_POOL_H
#define THREAD_POOL_H

#include <vector>
#include <queue>
#include <thread>
#include <atomic>
#include <condition_variable>
#include <future>

namespace ThreadPool
{
#define MAX_THREAD_NUM 8

//线程池,可以提交变参函数或lambda表达式的匿名函数执行,可以获取执行返回值
//支持类成员函数,支持类静态成员函数或全局函数,Operator()函数等
class ThreadPool
{
	typedef std::function<void()> Task;
private:
	std::vector<std::thread> m_pool;     // 线程池
	std::queue<Task> m_tasks;    // 任务队列
	std::mutex m_lock;    // 同步锁
	std::condition_variable m_cv;   // 条件阻塞
	std::atomic<bool> m_isStoped;    // 是否关闭提交
	std::atomic<int> m_idleThreadNum;  //空闲线程数量
public:
	ThreadPool(int size = MAX_THREAD_NUM) : m_isStoped(false)
	{
		size = size > MAX_THREAD_NUM ? MAX_THREAD_NUM : size;
		m_idleThreadNum = size;
		for (int i = 0; i < size; i++)
		{
			//初始化线程数量
			m_pool.emplace_back(&ThreadPool::scheduler, this);
		}
	}

	~ThreadPool()
	{
		Close();
		while (!m_tasks.empty())
		{
			m_tasks.pop();
		}
		m_cv.notify_all();  // 唤醒所有线程执行
		for (std::thread& thread : m_pool)
		{
			if (thread.joinable())
			{
				thread.join();  // 等待任务结束,前提是线程一定会执行完
			}
		}
		m_pool.clear();
	}

	// 打开线程池,重启任务提交
	void ReOpen()
	{
		if (m_isStoped) m_isStoped.store(false);
		m_cv.notify_all();
	}

	// 关闭线程池,停止提交新任务
	void Close()
	{
		if (!m_isStoped) m_isStoped.store(true);
	}

	// 判断线程池是否被关闭
	bool IsClosed() const
	{
		return m_isStoped.load();
	}

	// 获取当前任务队列中的任务数
	int GetTaskSize()
	{
		return m_tasks.size();
	}

	// 获取当前空闲线程数
	int IdleCount()
	{
		return m_idleThreadNum;
	}

	// 提交任务并执行
	// 调用方式为 std::future<returnType> var = threadpool.Submit(...)
	// var.get() 会等待任务执行完,并获取返回值
	// 其中 ... 可以直接用函数名+函数参数代替,例如 threadpool.Submit(f, 0, 1)
	// 但如果要调用类成员函数,则最好用如下方式
	// threadpool.Submit(std::bind(&Class::Func, &classInstance)) 或
	// threadpool.Submit(std::mem_fn(&Class::Func), &classInstance)
	template<class F, class... Args>
	auto Submit(F&& f, Args&&... args)->std::future<decltype(f(args...))>
	{
		if (m_isStoped.load())
		{
			throw std::runtime_error("ThreadPool is closed, can not submit task.");
		}

		using RetType = decltype(f(args...));  // typename std::result_of<F(Args...)>::type, 函数 f 的返回值类型
		std::shared_ptr<std::packaged_task<RetType()>> task = std::make_shared<std::packaged_task<RetType()>>(
		    std::bind(std::forward<F>(f), std::forward<Args>(args)...)
		);
		std::future<RetType> future = task->get_future();
		// 封装任务并添加到队列
		addTask([task]()
		{
			(*task)();
		});

		return future;
	}
private:
	// 消费者
	Task getTask()
	{
		std::unique_lock<std::mutex> lock(m_lock); // unique_lock 相比 lock_guard 的好处是:可以随时 unlock() 和 lock()
		while (m_tasks.empty() && !m_isStoped)
		{
			m_cv.wait(lock);
		}  // wait 直到有 task
		if (m_isStoped)
		{
			return Task();
		}
		assert(!m_tasks.empty());
		Task task = std::move(m_tasks.front()); // 取一个 task
		m_tasks.pop();
		m_cv.notify_one();
		return task;
	}

	// 生产者
	void addTask(Task task)
	{
		std::lock_guard<std::mutex> lock{ m_lock }; //对当前块的语句加锁, lock_guard 是 mutex 的 stack 封装类,构造的时候 lock(),析构的时候 unlock()
		m_tasks.push(task);
		m_cv.notify_one(); // 唤醒一个线程执行
	}

	// 工作线程主循环函数
	void scheduler()
	{
		while (!m_isStoped.load())
		{
			// 获取一个待执行的 task
			Task task(getTask());
			if (task)
			{
				m_idleThreadNum--;
				task();
				m_idleThreadNum++;
			}
		}
	}
};
}
#endif

测试函数

#include "threadpool.h"
#include <iostream>

struct gfun
{
	int operator()(int n)
	{
		printf("%d  hello, gfun !  %d\n", n, std::this_thread::get_id());
		return 42;
	}
};
#if defined(APPLE) || defined(__APPLE__) || defined (__apple__)
#include <sstream>
#endif
class Test
{
public:
	int GetThreadId(std::string a, double b)
	{
		std::this_thread::sleep_for(std::chrono::milliseconds(10000));
		std::thread::id i = std::this_thread::get_id();
		std::cout << "In Test, thread id: " << i << std::endl;
		std::cout << "a: " << a.c_str() << ", b = " << b << std::endl;

#if defined(APPLE) || defined(__APPLE__) || defined (__apple__) //for apple
		std::stringstream ss;
		ss << std::this_thread::get_id();
		int id = std::stoi(ss.str());
		return id;
#else
		return i.hash();
#endif
	}
};

int main()
{
	ThreadPool::ThreadPool worker{ 4 };
	Test t;
	std::cout << "at the beginning: " << std::endl;
	std::cout << "idle threads: " << worker.IdleCount() << std::endl;
	std::cout << "tasks: " << worker.GetTaskSize() << std::endl;
	std::future<int> f1 = worker.Submit(std::bind(&Test::GetThreadId, &t, "123", 456.789));

	std::cout << "after submit 1 task: " << std::endl;
	std::cout << "idle threads: " << worker.IdleCount() << std::endl;
	std::cout << "tasks: " << worker.GetTaskSize() << std::endl;
	std::future<int> f2 = worker.Submit(std::mem_fn(&Test::GetThreadId), &t, "789", 123.456);

	std::cout << "after submit 2 task: " << std::endl;
	std::cout << "idle threads: " << worker.IdleCount() << std::endl;
	std::cout << "tasks: " << worker.GetTaskSize() << std::endl;
	std::future<int> f3 = worker.Submit(gfun{}, 0);

	std::cout << "f1 = " << f1.get() << ", f2 = " << f2.get() << ", f3 = " << f3.get() << std::endl;

	std::cout << "after all task: " << std::endl;
	std::cout << "idle threads: " << worker.IdleCount() << std::endl;
	std::cout << "tasks: " << worker.GetTaskSize() << std::endl;
	return 0;
}

参考资料

  1. c++11多线程与线程池
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现一个简单的线程池,可以参考以下步骤和代码示例: 1. 首先,创建一个ThreadPool类,并包含必要的头文件和成员变量。引用中的代码提供了一个简单的线程池类的实现,其中包括线程队列、任务队列、互斥锁和条件变量等。 2. 在构造函数中初始化线程池,设置最小线程数和最大线程数,并创建工作线程和管理者线程。引用中的代码示例展示了如何初始化线程池,包括创建线程、启动工作线程和管理者线程。 3. 实现添加任务的方法,用于向任务队列中添加任务。这个方法将会唤醒等待的线程进行任务的执行。例如,在ThreadPool类中实现addTask方法,将任务添加到任务队列中。 4. 实现任务的执行方法。这个方法被工作线程调用,从任务队列中获取任务并执行。可以在ThreadPool类中实现runTask方法来执行任务。引用中的代码示例展示了如何执行任务。 5. 实现线程池的关闭方法。这个方法将会关闭线程池,并等待所有线程退出。在ThreadPool类中实现stop方法,用于关闭线程池。可以通过设置标志位来控制线程的退出,等待所有线程退出后再回收资源。 综上所述,C语言中可以通过使用互斥锁、条件变量和线程创建函数来实现一个简单的线程池。在构造函数中初始化线程池,包括创建工作线程和管理者线程。通过添加任务和执行任务的方法,实现对任务的管理和执行。最后,通过关闭线程池的方法来退出所有线程并回收资源。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++ 实现一个简易的线程池](https://blog.csdn.net/junmangbo4166/article/details/104806037)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [一文带你使用c++实现线程池!!!](https://blog.csdn.net/qq_44778436/article/details/128497327)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值