Начать поток с помощью функции-члена
Я пытаюсь построить std::thread
с функцией-членом, которая не принимает аргументов и возвращает void
. Я не могу понять какой-либо синтаксис, который работает - компилятор жалуется, что бы ни случилось. Каков правильный способ реализации spawn()
, чтобы он возвращал std::thread
, который выполняет test()
?
#include <thread>
class blub {
void test() {
}
public:
std::thread spawn() {
return { test };
}
};
Ответы
Ответ 1
#include <thread>
#include <iostream>
class bar {
public:
void foo() {
std::cout << "hello from member function" << std::endl;
}
};
int main()
{
std::thread t(&bar::foo, bar());
t.join();
}
EDIT:
Учет вашего редактирования, вы должны сделать это следующим образом:
std::thread spawn() {
return std::thread(&blub::test, this);
}
UPDATE: Я хочу объяснить еще несколько моментов, некоторые из них также обсуждались в комментариях.
Синтаксис, описанный выше, определен в терминах определения INVOKE (§20.8.2.1):
Определите INVOKE (f, t1, t2,..., tN) следующим образом:
- (t1. * f) (t2,..., tN), когда f является указателем на функцию-член класса T и t1 является объектом типа T или ссылкой на объект тип T или ссылку на объект типа, полученного из T;
- ((* t1). * f) (t2,..., tN), когда f является указателем на функцию-член класса T и t1 не является одним из типов, описанных в предыдущем пункт;
- t1. * f, когда N == 1 и f - указатель на данные элемента класса T, а t 1 - объект типа T или ссылка на объект типа T или ссылку на объект тип, полученный из T;
- (* t1). * f, когда N == 1 и f является указателем на данные элемента класса T и t 1 не является одним из типов, описанных в предыдущем элементе;
- f (t1, t2,..., tN) во всех остальных случаях.
Другим общим фактом, который я хочу отметить, является то, что по умолчанию конструктор потока копирует все переданные ему аргументы. Причина этого в том, что аргументам может потребоваться пережить вызывающий поток, и копирование аргументов гарантирует это. Вместо этого, если вы хотите действительно передать ссылку, вы можете использовать std::reference_wrapper
, созданный std::ref
.
std::thread (foo, std::ref(arg1));
Таким образом, вы обещаете, что будете заботиться о том, чтобы аргументы сохранялись, когда поток работает на них.
Обратите внимание, что все перечисленные выше могут также применяться к std::async
и std::bind
.
Ответ 2
Поскольку вы используете С++ 11, лямбда-выражение является хорошим и чистым решением.
class blub {
void test() {}
public:
std::thread spawn() {
return std::thread( [this] { this->test(); } );
}
};
так как this->
можно опустить, его можно сократить до:
std::thread( [this] { test(); } )
или просто
std::thread( [=] { test(); } )
Ответ 3
Вот полный пример
#include <thread>
#include <iostream>
class Wrapper {
public:
void member1() {
std::cout << "i am member1" << std::endl;
}
void member2(const char *arg1, unsigned arg2) {
std::cout << "i am member2 and my first arg is (" << arg1 << ") and second arg is (" << arg2 << ")" << std::endl;
}
std::thread member1Thread() {
return std::thread([=] { member1(); });
}
std::thread member2Thread(const char *arg1, unsigned arg2) {
return std::thread([=] { member2(arg1, arg2); });
}
};
int main(int argc, char **argv) {
Wrapper *w = new Wrapper();
std::thread tw1 = w->member1Thread();
std::thread tw2 = w->member2Thread("hello", 100);
tw1.join();
tw2.join();
return 0;
}
Компиляция с помощью g++ дает следующий результат:
g++ -Wall -std=c++11 hello.cc -o hello -pthread
i am member1
i am member2 and my first arg is (hello) and second arg is (100)
Ответ 4
@hop5 и @RnMss предложили использовать lambdas С++ 11, но если вы имеете дело с указателями, вы можете использовать их напрямую:
#include <thread>
#include <iostream>
class CFoo {
public:
int m_i = 0;
void bar() {
++m_i;
}
};
int main() {
CFoo foo;
std::thread t1(&CFoo::bar, &foo);
t1.join();
std::thread t2(&CFoo::bar, &foo);
t2.join();
std::cout << foo.m_i << std::endl;
return 0;
}
выходы
2
Переписанный образец из этого ответа был бы следующим:
#include <thread>
#include <iostream>
class Wrapper {
public:
void member1() {
std::cout << "i am member1" << std::endl;
}
void member2(const char *arg1, unsigned arg2) {
std::cout << "i am member2 and my first arg is (" << arg1 << ") and second arg is (" << arg2 << ")" << std::endl;
}
std::thread member1Thread() {
return std::thread(&Wrapper::member1, this);
}
std::thread member2Thread(const char *arg1, unsigned arg2) {
return std::thread(&Wrapper::member2, this, arg1, arg2);
}
};
int main() {
Wrapper *w = new Wrapper();
std::thread tw1 = w->member1Thread();
tw1.join();
std::thread tw2 = w->member2Thread("hello", 100);
tw2.join();
return 0;
}
Ответ 5
Некоторые пользователи уже дали свой ответ и объяснили это очень хорошо.
Я хотел бы добавить еще несколько вещей, связанных с потоком.
-
Как работать с функтором и потоком.
Пожалуйста, обратитесь к приведенному ниже примеру.
-
Поток будет выполнять свою собственную копию объекта при передаче объекта.
#include<thread>
#include<Windows.h>
#include<iostream>
using namespace std;
class CB
{
public:
CB()
{
cout << "this=" << this << endl;
}
void operator()();
};
void CB::operator()()
{
cout << "this=" << this << endl;
for (int i = 0; i < 5; i++)
{
cout << "CB()=" << i << endl;
Sleep(1000);
}
}
void main()
{
CB obj; // please note the address of obj.
thread t(obj); // here obj will be passed by value
//i.e. thread will make it own local copy of it.
// we can confirm it by matching the address of
//object printed in the constructor
// and address of the obj printed in the function
t.join();
}
Другой способ достижения того же: "
void main()
{
thread t((CB()));
t.join();
}
Но если вы хотите передать объект по ссылке, используйте синтаксис ниже:
void main()
{
CB obj;
//thread t(obj);
thread t(std::ref(obj));
t.join();
}