线程池 3(第三部分--线程池主体设计)

这一部分主要就是线程池的实现,然后写一个对外接口,将任务提交到任务队列,然后用多线程理任务。(这一部分主要内容就是任务函数的返回类型解析,多入参函数的解析,要确保多个入参可以被线程池解析出来,并且执行)。

这里的函数主要用到的是 <functional> 这个类,利用std::bind()将函数和参数进行绑定。参数使用c++11的新特性,可变参数模板。就可以实现。

class ThreadPool
{
public:
	//线程池构造函数
	ThreadPool();
	//1, 提交函数
	template<typename F, typename... Args>
	auto Enqueue(F&& f, Args &&...args)->std::future<decltype(f(args...))> ;

	// 析构函数
	inline ~ThreadPool();

private:
	// 线程池
	std::vector<std::thread> worker_;
	// 任务队列
	BoundedQueue<std::function<void()>>  task_queue_;
	// 退出线程池标志位
	std::atomic_bool stop_;

};

1、构造函数

//线程池构造函数
	explicit  ThreadPool(std::size_t thread_mun, std::size_t max_task_num = 1000) : stop_(false)
	{
		// 初始化失败抛出异常
		if (!task_queue_.Init(max_task_num, new SleepWaitStratrgy)) {	// SleepWaitStratrgy  BlockWaitStrategy
			throw std::runtime_error("Task queue init failed");
		}

		// 存放多个 std::thread线程对象
		worker_.reserve(thread_mun);
		for (size_t i = 0; i < thread_mun; i++)
		{
			// 使用lamdba 表达式来创建每个线程
			// 功能是 从任务队列中获取任务,并且执行任务的函数对象
			worker_.emplace_back([this] {
				while (!stop_)
				{
					std::function<void()> task;
					if (task_queue_.WaitDequeue(&task)) {
						task();
					}
				}
			});
		}
	}

1、对任务队列进行初始化。

2、修改一个线程对象大小。

3、在线程队列中,构造线程,并且轮流从线程池中用WaitDequeue(&task)函数,获取任务。并且运行任务。


2、提交任务函数

template<typename F, typename... Args>
	auto Enqueue(F&& f, Args &&...args)->std::future<decltype(f(args...))> {

		std::function<decltype(f(args...))()> func = 
             std::bind(std::forward<F>(f), std::forward<Args>(args)...);

		auto task = 
            std::make_shared<std::packaged_task<decltype(f(args...))()>>(func);

		// 任务队列的入队   并且唤醒线程
		task_queue_.Enqueue([task]() {(*task)();});

		return task->get_future();
	}

这个也是核心关键部分。

1、头部函数模板是利用的,可变参数模板来接受多个参数。

template<typename F, typename... Args>

2、这个头部函数名

auto Enqueue(F&& f, Args &&...args)->std::future<decltype(f(args...))> 

auto:动推导函数类型。

Enqueue(F&& f, Args &&...args):两个入参,一个是万能引用,一个是可变参数模板。(这里的&&不是右值引用,万能引用可以简单理解为,当T是模板参数时,T&&的作用主要是保持值类别进行转发。)

->std::future<decltype(f(args...))> :c++11中关键字decltype解决了auto关键字只能对变量类型进行类型推导的缺陷。用于弥补auto,不能直接推导函数类型。

std::future<>:异步推导,需要用到的时候再调用结果,不需要函数阻塞再原地,等待decltype推导函数类型。

3、

std::function<decltype(f(args...))()> func = 
             std::bind(std::forward<F>(f), std::forward<Args>(args)...);

定义一个func   类型是通用函数模板类,用decltype去推导函数类型。再由std::function 中的bind函数,将函数和其参数进行绑定。这里用到了std::forward() 完美转发,是因为我们这里的 “ f ”,传过来的时候不是右值,是一个"引用",然而,一个绑定到  完美引用 上的对象可能具 是个左值 或者也可能是个右值,正是因为有这种二义性,所以产生了std::forward

4、

auto task = std::make_shared<std::packaged_task<decltype(f(args...))()>>(func);

这里我们使用了std::make_shared方法,声明了一个std::packaged_task<decltype(f(args...))()>类型的智能指针,并且将前面的 func 作为std::package_task的参数传入。智能指针更方便的我们对package_task进行管理。

package_tas可以用来封装任何可以调用的目标,从而用于实现异步的调用。

5、

task_queue_.Enqueue([task]() {(*task)();});

return task->get_future();

任务入队,然后调用,task的get_future() 获取异步结果的方法。

我们返回了一个 future 对象,因此我们可以将其返回给调用者,由调用者自行决定何时调用 get() 方法获取异步操作的结果


3、析构函数

	// 析构函数
	inline ~ThreadPool() {
		// 防止重复析构, (将stop_改变成true,并且返回之前的值)
		if (stop_.exchange(true)) {
			return;
		}
		// 线程池中所有线程停止
		task_queue_.BreakAllWait();
		// 并且等待他们完成所有任务
		for (std::thread& worker : worker_)
		{
			worker.join();
		}
	}

这里的析构基本上就是基本的函数析构逻辑,解放所有的资源。

到此为止,整个线程池也就全部结束了。

 参考:基于C++11实现线程池 - 知乎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果你需要同时连接多个S7-1200并实现读写,可以使用线程池来实现。线程池是一组预先创建的线程,可以用于执行多个并发任务。以下是一个示例代码,可以使用线程池连接多个S7-1200并从它们读取数据: ```python import snap7 from concurrent.futures import ThreadPoolExecutor # 定义连接S7-1200的函数 def connect_plc(ip, port): plc = snap7.client.Client() plc.connect(ip, port) return plc # 定义读取S7-1200数据的函数 def read_data(plc): data = plc.read_area(snap7.types.Areas.DB, 1, 0, 10) return data # 定义S7-1200的IP地址和端口号 plc_ips = ['192.168.0.10', '192.168.0.20'] plc_ports = [102, 102] # 创建线程池,并连接到S7-1200 with ThreadPoolExecutor(max_workers=len(plc_ips)) as executor: plcs = list(executor.map(connect_plc, plc_ips, plc_ports)) # 读取S7-1200的数据 with ThreadPoolExecutor(max_workers=len(plcs)) as executor: data_list = list(executor.map(read_data, plcs)) # 输出读取的数据 for i, data in enumerate(data_list): print('Data from PLC', i+1, ':', data) # 关闭连接 for plc in plcs: plc.disconnect() ``` 在这个示例中,我们首先定义了两个S7-1200的IP地址和端口号,并将它们存储在plc_ips和plc_ports变量中。然后,我们使用线程池连接到这些S7-1200,并将连接的结果存储在plcs列表中。接下来,我们使用线程池从每个S7-1200的DB1区域读取了10个字节的数据,并将其存储在data_list列表中。最后,我们输出读取的数据并关闭连接。 值得注意的是,我们使用max_workers参数来设置线程池的最大工作线程数。在这个示例中,我们设置它为连接的S7-1200数量。这样可以确保我们的程序不会创建过多的线程,从而导致系统资源不足。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值