1. thread对象的析构问题
在 C++ 多线程标准库中,创建 thread 对象后,必须在对象析构前决定是 detach 还是 join。若在 thread 对象销毁时仍未做出决策,程序将会终止。
然而,在创建 thread 对象后、调用 join 前的代码中,若程序抛出异常,就会跳过 join 的调用,进而导致程序终止。
因此,必须在异常捕获中也调用 join。
这无疑增加了编程的复杂性,因为每个相关位置都需要在正常流程中写一次 join,在异常捕获中再写一次。
下面的代码将演示这一情况:
#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方法。
下面的代码展示了这一点:
#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对象析构时不同的处理,这里使用了策略模式。通过提供不同的策略类,就可以扩展出不同的析构行为。
同时,目前实现的策略类没有自己的成员函数,所以采用了单例模式来创建,避免创建出大量相同的对象而造成内存浪费。
最后,通过简单工厂模式来获取策略类。
下面展示一下具体的代码:
#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。
运行结果如下图所示:
以上就是本文的全部内容