web-dev-qa-db-fra.com

Démarrer le fil avec une fonction membre

J'essaie de construire un std::thread avec une fonction membre qui ne prend aucun argument et retourne void. Je ne peux trouver aucune syntaxe qui fonctionne - le compilateur se plaint quoi qu'il arrive. Quelle est la bonne façon d'implémenter spawn() pour qu'il retourne un std::thread qui exécute test()?

#include <thread>
class blub {
  void test() {
  }
public:
  std::thread spawn() {
    return { test };
  }
};
257
abergmeier
#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: Comptabiliser votre édition, vous devez le faire comme ceci:

  std::thread spawn() {
    return std::thread(&blub::test, this);
  }

PDATE: Je veux expliquer quelques points supplémentaires, dont certains ont également été discutés dans les commentaires.

La syntaxe décrite ci-dessus est définie en termes de définition INVOKE (§20.8.2.1):

Définissez INVOKE (f, t1, t2, ..., tN) comme suit:

  • (t1. * f) (t2, ..., tN) lorsque f est un pointeur sur une fonction membre d'une classe T et t1 est un objet de type T ou une référence à un objet de type T ou une référence à un objet d'un type dérivé de T;
  • ((* t1). * f) (t2, ..., tN) lorsque f est un pointeur sur une fonction membre d'une classe T et que t1 n'est pas l'un des types décrits dans l'item précédent;
  • t1. * f lorsque N == 1 et f est un pointeur sur les données de membre d'une classe T et t 1 est un objet de type T ou a
    référence à un objet de type T ou à un objet de type
    type dérivé de T;
  • (* t1). * f lorsque N == 1 et f est un pointeur sur les données de membre d'une classe T et t 1 n'est pas l'un des types décrits dans l'item précédent;
  • f (t1, t2, ..., tN) dans tous les autres cas.

Un autre fait général que je tiens à souligner est que, par défaut, le constructeur de thread copiera tous les arguments qui lui sont transmis. La raison en est que les arguments peuvent avoir besoin de survivre au thread d'appel, la copie des arguments le garantit. Au lieu de cela, si vous voulez vraiment passer une référence, vous pouvez utiliser un std::reference_wrapper créé par std::ref.

std::thread (foo, std::ref(arg1));

En faisant cela, vous promettez de prendre soin de garantir que les arguments existeront quand le thread les utilisera.


Notez que toutes les choses mentionnées ci-dessus peuvent également être appliquées à std::async et std::bind.

328
inf

Puisque vous utilisez C++ 11, lambda-expression est une solution agréable et propre.

class blub {
    void test() {}
  public:
    std::thread spawn() {
      return std::thread( [this] { this->test(); } );
    }
};

puisque this-> peut être omis, vous pouvez le raccourcir à:

std::thread( [this] { test(); } )

ou juste

std::thread( [=] { test(); } )
85
RnMss

Voici un exemple complet

#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;
}

Compiler avec g ++ donne le résultat suivant

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)
27
hop5

@ hop5 et @RnMss ont suggéré d'utiliser les lambdas C++ 11, mais si vous utilisez des pointeurs, vous pouvez les utiliser directement:

#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;
}

les sorties

2

Un échantillon réécrit de cette réponse serait alors:

#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;
}
14

Certains utilisateurs ont déjà donné leur réponse et l'ont très bien expliquée.

Je voudrais ajouter quelques choses plus liées au fil.

  1. Comment travailler avec foncteur et thread. Veuillez vous référer à l'exemple ci-dessous.

  2. Le fil va faire sa propre copie de l'objet en passant l'objet.

    #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();
    }
    

Une autre façon de réaliser la même chose est la suivante:

void main()
{
    thread t((CB()));

    t.join();
}

Mais si vous voulez transmettre l'objet par référence, utilisez la syntaxe ci-dessous:

void main()
{
    CB obj;
    //thread t(obj);
    thread t(std::ref(obj));
    t.join();
}
0
Mohit