C++进阶(二)线程池、异常处理、协程

6 篇文章 0 订阅

layout: post
title: C++进阶(二)C++新特性:智能指针、右值引用、lambda、多线程操作、function和bind、可变模板参数
description: C++进阶(二)C++新特性:智能指针、右值引用、lambda、多线程操作、function和bind、可变模板参数
tag: C++


文章目录

线程池案例

zero_threadpool.h

#pragma once
//zero_threadpool.h
#ifndef ZERO_THREADPOOL_H
#define ZERO_THREADPOOL_H
#include <future>
#include <functional>
#include <iostream>
#include <queue>
#include <mutex>
#include <memory>
#ifdef WIN32
#include <windows.h>
#else
#include <time.h>
#endif
using namespace std;

void getNow(struct timeval* tv);
int64_t getNowMs();

#define TNOW getNow()
#define TNOWMS getNowMs()

/**
* @file zero_thread_pool.h
* @brief 线程池类,采用c++11来实现了,
* 使用说明:
* ZERO_ThreadPool tpool;
* tpool.init(5); //初始化线程池线程数
* //启动线程方式
* tpool.start();
* //将任务丢到线程池中
* tpool.exec(testFunction, 10); //参数和start相同
* //等待线程池结束
* tpool.waitForAllDone(1000); //参数<0时, 表示无限等待(注意有人调用stop也会退出)
* //此时: 外部需要结束线程池是调用
* tpool.stop();
* 注意:
* ZERO_ThreadPool::exec执行任务返回的是个future, 因此可以通过future异步获取结果, 比如:
* int testInt(int i)
* {
* return i;
* }
* auto f = tpool.exec(testInt, 5);
* cout << f.get() << endl; //当testInt在线程池中执行后, f.get()会返回数值5
*
* class Test
* {
* public:
* int test(int i);
* };
* Test t;
* auto f = tpool.exec(std::bind(&Test::test, &t, std::placeholders::_1), 10);
* //返回的future对象, 可以检查是否执行
* cout << f.get() << endl;
*/
class ZERO_ThreadPool
{
protected:
	struct TaskFunc
	{
		TaskFunc(uint64_t expireTime) : _expireTime(expireTime)
		{ }

		std::function<void()> _func;
		int64_t _expireTime = 0;  //超时的绝对时间
	};
	typedef shared_ptr<TaskFunc> TaskFuncPtr;
public:
	/**
	*构造函数
	*/
	ZERO_ThreadPool();

	/**
	*析构函数,停止所有线程
	*/
	virtual ~ZERO_ThreadPool();
	
	/*
	工作线程个数
	*/
	bool init(size_t num);

	/*
	获取线程个数
	*/
	size_t getThreadNum()
	{
		std::unique_lock<std::mutex> lock(_mutex);
		return _threads.size();
	}

	/*
	获取当前线程池的任务数
	*/
	size_t getJobNum()
	{
		std::unique_lock<std::mutex> lock(_mutex);
		return _tasks.size();
	}

	/*
	停止所有线程
	*/
	void stop();

	/*
	启动所有线程
	*/
	bool start();

	/*
	用线程池启动任务(F是function, Args是参数)
	返回任务的future对象,可以通过这个对象来获取返回值
	*/
	template <class F, class... Args>
	auto exec(F&& f, Args&&...args) -> std::future<decltype(f(args...))>
	{
		return exec(0, f, args...);
	}

	/*
	用线程池启动任务(F是function,Args是参数)
	timeoutMs:超时时间,单位ms(为0时不做超时控制),若任务超时,任务将被抛弃
	返回任务的future对象,可以通过这个对象来获取返回值
	*/
	template <class F, class... Args>
	auto exec(int64_t timeoutMs, F&& f, Args&&... args) -> std::future<decltype(f(args...))>
	{
		int64_t expireTime = (timeoutMs == 0 ? 0 : TNOWMS + timeoutMs); // 获取现在时间
		// 定义返回值类型
		using RetType = decltype(f(args...));
		// 封装任务
		auto task = std::make_shared<std::packaged_task<RetType()>>(std::bind(std::forward<F>(f), std::forward<Args(args)...>));
		// 封装任务指针,设置过期时间
		TaskFuncPtr fPtr = std::make_shared<TaskFunc>(expireTime);
		// 具体执行的函数
		fPtr->_func = [task]() {
			(*task)();
		};

		std::unique_lock<std::mutex> lock(_mutex);
		_tasks.push(fPtr);  // 插入任务
		_condition.notify_one();  //  唤醒阻塞的线程,可以考虑只有任务队列为空的情况再去notify
		return task->get_future();
	}

	/*
	等待当前任务队列中,所有工作全部结束(队列无任务)
	millsecond 等待的时间(ms),-1,:永远等待
	*/
	bool waitForAllDone(int millsecond = -1);

protected:
	/*
	获取任务
	return TaskFuncPtr
	*/
	bool get(TaskFuncPtr& task);

	/*
	线程池是否退出
	*/
	bool isTerminate() { return _bTerminate; }

	void run();

protected:
	/*
	任务队列
	*/
	queue<TaskFuncPtr> _tasks;

	/*
	工作线程
	*/
	std::vector<std::thread*> _threads;

	/*
	线程互斥量
	*/
	std::mutex _mutex;

	/*
	线程条件变量
	*/
	std::condition_variable _condition;

	/*
	线程数
	*/
	size_t _threadNum;

	/*
	终止变量
	*/
	bool _bTerminate;

	/*
	原子变量
	*/
	std::atomic<int> _atomic{ 0 };

};

#endif

zero_threadpool.cpp

#include "zero_threadpool.h"

ZERO_ThreadPool::ZERO_ThreadPool() : _threadNum(1), _bTerminate(false) {}

ZERO_ThreadPool::~ZERO_ThreadPool() { stop(); }

bool ZERO_ThreadPool::init(size_t num)
{
	std::unique_lock<std::mutex> lock(_mutex);

	// 假如线程队列仍有线程在运行,直接返回false
	if (!_threads.empty())
	{
		return false;
	}

	_threadNum = num;
	return true;
}

void ZERO_ThreadPool::stop()
{
	{
		std::unique_lock<std::mutex> lock(_mutex);

		_bTerminate = true;

		_condition.notify_all();
	}

	for (size_t i = 0; i < _threads.size(); i++)
	{
		if (_threads[i]->joinable())
		{
			_threads[i]->join();
		}
		delete _threads[i];
		_threads[i] = nullptr;
	}

	std::unique_lock<std::mutex> lock(_mutex);
	_threads.clear();
}

bool ZERO_ThreadPool::start()
{
	std::unique_lock<std::mutex> lock(_mutex);

	if (!_threads.empty())
	{
		return false;
	}

	for (size_t i = 0; i < _threadNum; i++)
	{
		_threads.push_back(new thread(&ZERO_ThreadPool::run, this));
	}
	return true;
}

bool ZERO_ThreadPool::get(TaskFuncPtr& task)
{
	std::unique_lock<std::mutex> lock(_mutex);

	if (_tasks.empty())
	{
		_condition.wait(lock, [this] {return _bTerminate || !_tasks.empty(); });
	}
	
	if (_bTerminate)
	{
		return false;
	}

	if (!_tasks.empty())
	{
		task = std::move(_tasks.front());
		_tasks.pop();
		return true;
	}
	return false;
}

void ZERO_ThreadPool::run() // 执行任务的线程
{
	// 调用处理部分
	while (!isTerminate())
	{
		TaskFuncPtr task;
		bool ok = get(task); // 读取任务
		if (ok)
		{
			++_atomic;
			try
			{
				if (task->_expireTime != 0 && task->_expireTime < TNOWMS)
				{
					// 超时任务是否需要处理呢?
				}
				else 
				{
					task->_func(); // 执行任务
				}
			}
			catch (const std::exception&)
			{

			}
			--_atomic;

			// 任务都执行完毕了
			std::unique_lock<std::mutex> lock(_mutex);
			if (_atomic == 0 && _tasks.empty())
			{
				_condition.notify_all(); //  这里只是为了通知waitForAllDone
			}
		}
	}
}

bool ZERO_ThreadPool::waitForAllDone(int millsecond)
{
	std::unique_lock<std::mutex> lock(_mutex);

	if (_tasks.empty()) return true;

	if (millsecond < 0)
	{
		_condition.wait(lock, [this] { return _tasks.empty(); });
		return true;
	}
	else
	{
		return _condition.wait_for(lock, std::chrono::milliseconds(millsecond), [this] {return _tasks.empty(); });
	}
}

int gettimeofday(struct timeval& tv)
{
#if WIN32
	time_t clock;
	struct tm tm;
	SYSTEMTIME wtm;
	GetLocalTime(&wtm);
	tm.tm_year = wtm.wYear - 1900;
	tm.tm_mon = wtm.wMonth - 1;
	tm.tm_mday = wtm.wDay;
	tm.tm_hour = wtm.wHour;
	tm.tm_min = wtm.wMinute;
	tm.tm_sec = wtm.wSecond;
	tm.tm_isdst = -1;
	clock = mktime(&tm);
	tv.tv_sec = clock;
	tv.tv_usec = wtm.wMilliseconds * 1000;
	return 0;
#else
	return ::gettimeofday(tv);
#endif
}

void getNow(struct timeval& tv)
{
#if TARGET_PLATFORM_IOS || TARGET_PLATFORM_LINUX
	int idx = _buf_idx;
	*tv = _t[idx];
	if (fabs(_cpu_cycle - 0) < 0.0001 && _use_tsc)
	{
		addTimeOffset(*tv, idx);
	}
	else
	{
		TC_Common::gettimeofday(*tv);
	}
#else
	gettimeofday(tv);
#endif
}

int64_t getNowMs()
{
	struct timeval tv;
	getNow(tv);
	return tv.tv_sec * (int64_t)1000 + tv.tv_usec / 1000;
}


main.cpp

#include <iostream>
#include <../../zero_threadpool.h>
using namespace std;

void func0()
{
    cout << "func0()" << endl;
}

void func1(int a)
{
    cout << "func1 int =" << a << endl;
}

//void func1(string a)
//{
//    cout << "func1 string =" << a << endl;
//}

void func2(int a, string b)
{
    cout << "func2() a=" << a << ", b=" << b << endl;
}


void test1() // 简单测试线程池
{
    ZERO_ThreadPool threadpool;     // 封装一个线程池
    threadpool.init(1);             // 设置线程的数量
    threadpool.start();              // 启动线程池,创建线程, 线程没有start,创建完毕后被调度
    // 假如要执行的任务
//    threadpool.exec(1000,func0); // 1000是超时1000的意思,目前没有起作用
    threadpool.exec(func1, 10);
    //    threadpool.exec((void(*)(int))func1, 10);   // 插入任务
    //    threadpool.exec((void(*)(string))func1, "king");
    threadpool.exec(func2, 20, "darren"); // 插入任务
    threadpool.waitForAllDone(); // 等待都执行完退出 运行函数 插入1000个任务, 等1000个任务执行完毕才退出
    threadpool.stop();      // 这里才是真正执行退出
}

int func1_future(int a)
{
    cout << "func1() a=" << a << endl;
    return a;
}

string func2_future(int a, string b)
{
    cout << "func1() a=" << a << ", b=" << b << endl;
    return b;
}

void test2() // 测试任务函数返回值
{
    ZERO_ThreadPool threadpool;
    threadpool.init(1);
    threadpool.start(); // 启动线程池
    // 假如要执行的任务
    std::future<decltype (func1_future(0))> result1 = threadpool.exec(func1_future, 10);
    std::future<string> result2 = threadpool.exec(func2_future, 20, "darren");
    //  auto result2 = threadpool.exec(func2_future, 20, "darren");

    std::cout << "result1: " << result1.get() << std::endl;
    std::cout << "result2: " << result2.get() << std::endl;
    threadpool.waitForAllDone();
    threadpool.stop();
}

class Test
{
public:
    int test(int i) {
        cout << _name << ", i = " << i << endl;
        return i;
    }
    //    int test(string str) {
    //        cout << _name << ", str = " << str << endl;
    //    }
    void setName(string name) {
        _name = name;
    }
    string _name;
};

void test3() // 测试类对象函数的绑定
{
    ZERO_ThreadPool threadpool;
    threadpool.init(1);
    threadpool.start(); // 启动线程池
    Test t1;
    Test t2;
    t1.setName("Test1");
    t2.setName("Test2");
    auto f1 = threadpool.exec(std::bind(&Test::test, &t1, std::placeholders::_1), 10);
    auto f2 = threadpool.exec(std::bind(&Test::test, &t2, std::placeholders::_1), 20);
    threadpool.waitForAllDone();
    cout << "t1 " << f1.get() << endl;
    cout << "t2 " << f2.get() << endl;
}

void func2_1(int a, int b)
{
    cout << "func2_1 a + b = " << a + b << endl;
}

int func2_1(string a, string b)
{
    cout << "func2_1 a + b = " << a << b << endl;
    return 0;
}
void test4() // 简单测试线程池
{
    ZERO_ThreadPool threadpool;     // 封装一个线程池
    threadpool.init(1);             // 设置线程的数量
    threadpool.start();              // 启动线程池,创建线程, 线程没有start,创建完毕后被调度
    // 假如要执行的任务
    threadpool.exec((void(*)(int, int))func2_1, 10, 20);   // 插入任务
    threadpool.exec((int(*)(string, string))func2_1, "king", " and darren");
    threadpool.waitForAllDone(); // 等待都执行完退出 运行函数 插入1000个任务, 等1000个任务执行完毕才退出
    threadpool.stop();      // 这里才是真正执行退出
}

int main()
{
    //    test1(); // 简单测试线程池
    //    test2(); // 测试任务函数返回值
    //    test3(); // 测试类对象函数的绑定
    test4();
    cout << "main finish!" << endl;
    return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值