Реми опубликовал отличное решение для передачи любой функции с любым количеством аргументов в std::thread
, здесь. Мне было интересно, как его можно использовать для функций-членов класса.
#include <iostream>
#include <thread>
#include <mutex>
#define __PRETTY_FUNCTION__ __FUNCSIG__
std::mutex my_mutex;
template<class Function, class... Args>
void runFunctionInThread(Function f, Args&&... args) {
// Beside starting a thread, this function performs other
// task using the function and its arguments.
std::thread t(f, std::forward<Args>(args)...);
t.detach();
}
class MyClass
{
public:
MyClass() {};
void myFunc1() { std::unique_lock<std::mutex> lock(my_mutex); std::cout << __PRETTY_FUNCTION__ << "\n"; }
void myFunc2(int value) { std::unique_lock<std::mutex> lock(my_mutex); std::cout << __PRETTY_FUNCTION__ << "value is " << value << "\n"; }
void myFunc3(int value1, int value2) { std::unique_lock<std::mutex> lock(my_mutex); std::cout << __PRETTY_FUNCTION__ << "value1+value2 is " << value1 + value2 << "\n"; }
void manager() {
void (MyClass::*f1)() = &MyClass::myFunc1;
void (MyClass::*f2)(int) = &MyClass::myFunc2;
void (MyClass::*f3)(int,int) = &MyClass::myFunc3;
runFunctionInThread(f1);
runFunctionInThread(f2, 2);
runFunctionInThread(f3, 3, 3);
}
};
void isolatedFunc1() { std::unique_lock<std::mutex> lock(my_mutex); std::cout << __PRETTY_FUNCTION__ << "\n"; }
void isolatedFunc2(int value) { std::unique_lock<std::mutex> lock(my_mutex); std::cout << __PRETTY_FUNCTION__ << " value is " << value << "\n"; }
void isolatedFunc3(int value1, int value2) { std::unique_lock<std::mutex> lock(my_mutex); std::cout << __PRETTY_FUNCTION__ << " value1+value2 is " << value1 + value2 << "\n"; }
int main()
{
runFunctionInThread(&isolatedFunc1); // Works flawlessly
runFunctionInThread(&isolatedFunc2, 2); // Works flawlessly
runFunctionInThread(&isolatedFunc3, 3, 3); // Works flawlessly
MyClass m;
m.manager();
}
std::thread
уже делает это за вас. Ему просто нужно знать, какой член объекта вызывать. Передайте this
в качестве первого аргумента после указателя на функцию.
Это похоже на вариант использования std::async, а не std::thread
.
Какая часть или решение Реми не работает для вас? Первый аргумент std::thread
— это либо функция, либо указатель на функцию-член, и std::thread
поступает с этим правильно. Если вы хотите сделать что-то вроде того, что делает std::thread
для собственного использования, взгляните на std::invoke().
Если вы хотите вызвать функцию-член, вам также нужно передать объект std::thread
:
void manager() {
//...
runFunctionInThread(f1, this);
runFunctionInThread(f2, this, 2);
runFunctionInThread(f3, this, 3, 3);
}
Примечание: Я заметил, что вы отделяете свои темы. Затем вам нужно вручную убедиться, что они закончились раньше, чем ваша программа, иначе вас ждут сюрпризы при выходе из программы, если они все еще работают.
Часто проще хранить запущенные потоки в контейнере (например, std::list<std::thread>
) и join()
их позже. В C++20 можно даже сделать std::list<std::jthread>, чтобы они автоматически соединялись при уничтожении контейнера.
... и, очевидно, взять адрес функции-члена: в отличие от обычных функций, функции-члены не распадаются на указатель на функции-члены.
@DietmarKühl Если я правильно прочитал код OP:s, это то, что он делает, или я что-то упустил?
@TedLyngmo: да, действительно. Я подумал, что стоит указать на это, так как это неясно из ответа.
@DietmarKühl О, хорошо. Я тут забеспокоился :-)
Я полагаю, это упражнение, но обратите внимание, что создание потока и немедленный вызов
join
блокирует любой параллелизм. Ваш код квазипоследовательный