C++多线程编程——基于策略模式、单例模式和简单工厂模式的可扩展智能析构线程

1. thread对象的析构问题

在 C++ 多线程标准库中,创建 thread 对象后,必须在对象析构前决定是 detach 还是 join。若在 thread 对象销毁时仍未做出决策,程序将会终止。

然而,在创建 thread 对象后、调用 join 前的代码中,若程序抛出异常,就会跳过 join 的调用,进而导致程序终止。

因此,必须在异常捕获中也调用 join。

这无疑增加了编程的复杂性,因为每个相关位置都需要在正常流程中写一次 join,在异常捕获中再写一次。

下面的代码将演示这一情况:

cpp 复制代码
#include <iostream>
#include <thread>

using namespace std;

void threadFunc()
{
	cout << "Hello from thread" << endl;
}


int main()
{
	thread t(threadFunc);
	try
	{
		throw runtime_error("Something went wrong");
	}
	catch (...)
	{
		t.join();
		throw;
	}
	t.join();
}

2. 一种简单的解决办法------RAII

一种简单的解决办法就是使用RAII思想,编写一个类来绑定一个thread对象,在类的析构函数中调用thread对象的join方法。

下面的代码展示了这一点:

cpp 复制代码
#include <iostream>
#include <thread>

using namespace std;

class thread_guard
{
public:
    thread_guard(std::thread& t) : t_(t) {}
    ~thread_guard()
    {
        if (t_.joinable())
        {
            t_.join();
        }
    }
    thread_guard(const thread_guard&) = delete;
    thread_guard& operator=(const thread_guard&) = delete;
private:
    thread& t_;
};

void threadFunc()
{
    cout << "Thread function running..." << endl;
}

int main()
{
    thread t(threadFunc);
    thread_guard g(t);

    return 0;
}

局部对象会自动被销毁,在销毁时thread_guard类对象的析构函数会自动调用thread类对象的join方法,从而保证thread不会异常终止。

但是这种方法太死板了,只会调用join方法。

我们可能希望自己选择detach或者join,也可能想要在thread对象销毁时做一些别的事情。

出于这种想法,本文提出了一种可扩展的智能析构线程,下面将对其进行介绍。

3. 可扩展的智能析构线程

首先,对于thread对象析构时不同的处理,这里使用了策略模式。通过提供不同的策略类,就可以扩展出不同的析构行为。

同时,目前实现的策略类没有自己的成员函数,所以采用了单例模式来创建,避免创建出大量相同的对象而造成内存浪费。

最后,通过简单工厂模式来获取策略类。

下面展示一下具体的代码:

cpp 复制代码
#include <iostream>
#include <thread>


using namespace std;

class thread_destroy_strategy
{
public:
	virtual void destroy(thread& t)const = 0;
	virtual ~thread_destroy_strategy() = default;
};

class join_strategy : public thread_destroy_strategy
{
public:
	static join_strategy* getInstance()
	{
		static join_strategy instance;
		return &instance;
	}
	void destroy(thread& t)const override
	{
		if (t.joinable())
		{
			t.join();
			cout << "Thread " << this_thread::get_id() << " joined" << endl;
		}
	}
};

class detach_strategy : public thread_destroy_strategy
{
public:

	static detach_strategy* getInstance()
	{
		static detach_strategy instance;
		return &instance;
	}

	void destroy(thread& t)const override
	{
		if (t.joinable())
		{
			t.detach();
			cout << "Thread " << this_thread::get_id() << " detached" << endl;
		}
	}
};

enum class EThreadStrategy
{
	JOIN,
	DETACH
};

class strategyFactory
{
public:
	static thread_destroy_strategy* getStrategy(EThreadStrategy strategy)
	{
		switch (strategy)
		{
		case EThreadStrategy::JOIN:
			return join_strategy::getInstance();
		case EThreadStrategy::DETACH:
			return detach_strategy::getInstance();
		default:
			return nullptr;
		}
	}
};

class auto_thread
{
public:
	template<typename F, typename... Args>
	auto_thread(F&& f, Args&&... args) : t(forward<F>(f), forward<Args>(args)...) {}
	~auto_thread()
	{
		thread_destroy_strategy* pStrategy = strategyFactory::getStrategy(strategy);
		if (pStrategy)
		{
			pStrategy->destroy(t);
		}
	}
	auto_thread(const auto_thread&) = delete;
	auto_thread& operator=(const auto_thread&) = delete;

public:
	void setStrategy(EThreadStrategy strategy_)
	{
		strategy = strategy_;
	}
private:
	thread t;
	EThreadStrategy strategy = EThreadStrategy::JOIN;
};

void threadFunc()
{
	cout << "Hello from thread" << endl;
}


int main()
{
	auto_thread t(threadFunc);
	t.setStrategy(EThreadStrategy::JOIN); // 默认就是JOIN策略, 也可以设置为DETACH策略
}

策略类在destroy时打印了一下线程id。

运行结果如下图所示:

以上就是本文的全部内容

相关推荐
hb_zhyu4 分钟前
Acwing.基础课.排列数字(c++题解)
数据结构·c++·算法
一丝晨光43 分钟前
为什么会有函数调用参数带标签的写法?Swift函数调用的参数传递需要加前缀是否是冗余?函数调用?函数参数?
java·开发语言·c++·ios·c#·objective-c·swift
锐策2 小时前
『 C++ 』中不可重写虚函数的实用案例
开发语言·c++
*TQK*3 小时前
ZZNUOJ(C/C++)基础练习1051——1060(详解版)
c语言·c++
荣--4 小时前
C++学习:CRTP 模式是什么
c++·设计模式·模板类
CodeClimb4 小时前
【华为OD-E卷 - 任务最优调度 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
近听水无声4774 小时前
c++模板进阶
c++·学习
axxy20004 小时前
C++ Primer Plus第六章课后习题总结
数据结构·c++·算法