问题描述
我想创建一个辅助类(或 std::thread 的子类)以允许设置堆栈大小以及线程名称。 如果线程在不支持的平台上运行,例如堆栈大小,这个数字应该被忽略。
我在想一个“ThreadHelper”,它具有扩展的构造函数接口,并且只返回一个 std::thread。
老实说,我对 std::thread 包含的所有模板内容几乎没有经验。
现在线程类实例化如下: m_thread = new std::thread(&Button::Process,this); 我想要类似的东西: m_thread = new ThreadHelper.Create(&Button::Process,this,stackSize,name);
感谢任何建议。
谢谢 马丁
解决方法
您可以这样做: 您需要一个围绕线程启动函数的包装器,以便您可以在线程函数运行之前(也可能是之后)调用适当的函数。包含一个 try-catch 块并进行一些错误处理也可能是一个好主意。
template <typename ThreadStartFnc>
struct ThreadWrapper
{
ThreadStartFnc fnc;
const char *name; // (1) Note: careful,just a pointer here,not a string. See below.
size_t priority;
size_t stack_size;
ThreadWrapper(...) // constructor here...
void operator()()
{
SetThreadName(name); // Whatever that is on your system
SetThreadPriority(priority); // dito
SetStackSize(stack_size); // not all systems allow this
try {
fnc();
}
catch(...) {
cerr << "Exception caught"; // Do exception processing here.
}
}
};
并且您需要一种简单的方法来实例化带有“插入”包装器的 std::thread,如下所示:
template <typename Fnc>
std::thread make_thread(const Fnc& f,const char *name,size_t priority=0,size_t stack_size=0)
{
return std::thread(ThreadWrapper<Fnc>(f,name,priority,stack_size));
}
基本上就是这样。 std::thread 接受一个 Functor 对象,这就是 ThreadWrapper 。它调用 Functor() 来启动线程,也就是 void operator()()
。此函数使用附加参数 name、priority、stack_size 来设置所有内容,然后调用您的函数。
使用 C++11/14/17 好东西(如可变参数模板参数和/或 lambda 函数,随心所欲地增强它。
下面是我们使用的实际包装函数(虽然我们没有使用 Functor,但我们使用 boost::bind 和一个静态模板函数代替)。
template <typename Fnc>
static inline void run_cstr(const Fnc &f,DWORD priority)
{
int rc=_configthreadlocale(0);
/*
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
setlocale(LC_ALL,"C");
*/
SetThreadName(name);
watchdog::set_thread_info(name);
::SetThreadPriority(::GetCurrentThread(),priority);
_set_se_translator(&_se_translator);
_set_invalid_parameter_handler(&my_invalid_parameter_handler);
__try {
f();
}
__except (global_seh_handler(GetExceptionCode(),GetExceptionInformation()) ) {
}
}
它为 Visual Studio 设置 ThreadName (SetThreadName),设置区域设置,设置线程优先级,将我们的软件看门狗连接到线程,并为所有异常设置全局 Windows try/catch 处理程序,包括访问冲突等。 ..
如果任何未捕获的异常(包括无效的 std lib 参数)在此处结束,global_seh_handler
将被执行。它将编写我们用于事后调试的故障转储文件。
(1) 注意:我使用了 const char *name
作为线程名称,因为我假设线程将立即运行,而线程名称的字符串仍然可用。如果您将 ThreadWrapper 类型的对象存储更长的时间,那实际上是不安全的。那么您需要将 const char*
更改为 std::string
。