У меня есть требование учесть все потоки в C ++ / 17 'приложении, которое мы собираемся разработать;
Сам счетчик потоков является thread_local
объектом, который хранит информацию о потоке и может сообщать о нейвернуться к основному модулю.
Проблема в том, что thread_local
объекты должны вызываться из каждого потока, по крайней мере, один раз, чтобы их можно было даже построить (см .: Могу ли я иметь объект thread_local, который будет ctor / dtorвызывается при создании каждого потока, даже если он не используется? ).После этого он будет должным образом разрушен при выходе из потока.
Моя первоначальная мысль состоит в том, чтобы сделать такую конструкцию:
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.) Если мой подход можно исправить, как лучше это сделать?