如何在C ++中尽可能自动地用命名令牌标记每个线程?

时间:2018-12-06 18:12:26

标签: c++ multithreading c++17

我需要考虑我们将要开发的C ++ / 17'应用程序中的所有线程;

线程会计iteself是thread_local对象,用于保存有关线程的信息并能够将其报告回主模块。

问题是thread_local对象需要从每个线程至少调用一次,才能被构造(请参阅:Can I have thread_local object which ctor/dtor will be called on each thread creation even if it is not used?)。之后,它将在线程退出时被正确破坏。

我最初的想法是进行这样的构造:

thread_local thread_accounter_t thread_accounter; // This is main accounter object

void thread_started(std::string name, std::string function, std::string filename, int line)
{
   thread_accounter.thread_started(name, function, filename, line);
}
#define THREAD_STARTED(name) thread_started(name, __FUNCTION__, __FILE__, __LINE__)

然后,在每个线程中使用:

std::thread my_thread([](){
    THREAD_STARTED("example");
    // Do actual job
});

这种方式有一个很大的缺点:容易出错。每个不使用宏的线程(即由于忽略)对于会计人员都是不可见的。

我的下一个想法是继承std::thread并覆盖它的构造函数。这样,当某人使用our::thread时,他们(被构造方法)将被迫填充线程名称。通过使用简单的技巧,我们可以强制用户不要使用std::thread

class thread : public std::thread
{
   public:
       thread_t() noexcept {}
       thread_t( thread_t&& other ) noexcept : std::thread(std::move(other)) {}
       template<class function_t, class... args_t >

// Following constructor actually does not compile:
       explicit thread_t(std::string name, std::string function, std::string filename, int line, function_t&& f, args_t&&... args ) :
       std::thread(std::bind([name, function, filename, line](function_t&& f, auto&&... args){
           thread_started(name, function, filename, line);
           f(std::forward(args)...);
       }, std::move(f), std::move(args)...)) { }
       thread_t(const thread_t&) = delete;
};
#define THREAD_NAME(name) name, __FUNCTION__, __FILE__, __LINE__

然后使用它:

thread my_thread(THREAD_NAME("sample thread"), [](){
    // Do actual job
});

问题有两个:

1。)如果我的方法很好,如何修复thread的构造函数以使其可以编译? 最小样本:https://pastebin.com/mNMpunMT

2。)如果我的方法可以纠正,那么更好的方法是什么?

1 个答案:

答案 0 :(得分:0)

钉了!

由于std::bind构造函数可以接受任意参数,因此不需要std::thread。所以,这是最终版本:

#include <iostream>
#include <thread>

void thread_started(std::string name, std::string function, std::string filename, int line)
{

}

class thread_t : public std::thread
{
   public:
       thread_t() noexcept {}
       thread_t( thread_t&& other ) noexcept : std::thread(static_cast<std::thread&&>(std::move(other))) {}
       template<class function_t, class... args_t >

       explicit thread_t(std::string name, std::string function, std::string filename, int line, function_t&& f, args_t&&... args ) :
       std::thread([name, function, filename, line](function_t&& f, auto&&... args){
           thread_started(name, function, filename, line);
           f(std::move(args)...);
       }, std::move(f), std::move(args)...) { }
       thread_t(const thread_t&) = delete;
};
#define THREAD_NAME(name) name, __FUNCTION__, __FILE__, __LINE__

int main()
{
    thread_t my_thread(THREAD_NAME("sample thread"), [](){
        // Do actual job
       std::cout << "Hello!";
    });
    std::cout << "Hello, world!\n";
}