Thread mit Member-Funktion starten

Lesezeit: 8 Minuten

Thread mit Member Funktion starten
abergmeier

Ich versuche, eine zu konstruieren std::thread mit einer Member-Funktion, die keine Argumente akzeptiert und zurückgibt void. Ich kann keine Syntax herausfinden, die funktioniert – der Compiler beschwert sich, egal was passiert. Was ist die richtige Art der Umsetzung spawn() damit es a zurückgibt std::thread das ausführt test()?

#include <thread>
class blub {
  void test() {
  }
public:
  std::thread spawn() {
    return { test };
  }
};

  • Meinst du, die Funktion gibt void zurück, wird void genannt, oder sie hat einfach keine Parameter. Kannst du den Code für das hinzufügen, was du zu tun versuchst?

    – Zaid Amir

    20. Mai 2012 um 13:02 Uhr


  • Hast du getestet? (Habe ich noch nicht.) Ihr Code scheint sich auf RVO (Return-Value-Optimzation) zu verlassen, aber ich glaube nicht, dass Sie dies tun sollten. Ich denke, mit std::move( std::thread(func) ); ist besser, z std::thread hat keinen Kopierkonstruktor.

    – RnMss

    10. Oktober 2013 um 11:28 Uhr


  • @RnMss: Sie können sich auf RVO verlassen und verwenden std::move ist in diesem Fall überflüssig – wäre dies nicht der Fall und gäbe es keinen Kopierkonstruktor, würde der Compiler sowieso einen Fehler ausgeben.

    – Qualia

    22. Oktober 2015 um 13:19 Uhr

Thread mit Member Funktion starten
Stefan Dollberg

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

BEARBEITEN: Um Ihre Bearbeitung zu berücksichtigen, müssen Sie dies folgendermaßen tun:

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

AKTUALISIEREN: Ich möchte noch einige Punkte erläutern, einige davon wurden auch in den Kommentaren diskutiert.

Die oben beschriebene Syntax ist in Bezug auf die INVOKE-Definition (§20.8.2.1) definiert:

Definieren Sie INVOKE (f, t1, t2, …, tN) wie folgt:

  • (t1.*f)(t2, …, tN) wenn f ein Zeiger auf eine Elementfunktion einer Klasse T ist und t1 ein Objekt vom Typ T oder eine Referenz auf ein Objekt vom Typ T oder eine Referenz auf an ist Objekt eines von T abgeleiteten Typs;
  • ((*t1).*f)(t2, …, tN) wenn f ein Zeiger auf eine Elementfunktion einer Klasse T ist und t1 keiner der im vorherigen Punkt beschriebenen Typen ist;
  • t1.*f wenn N == 1 und f ein Zeiger auf Elementdaten einer Klasse T ist und t 1 ein Objekt vom Typ T oder a ist
    Referenz auf ein Objekt vom Typ T oder eine Referenz auf ein Objekt von a
    Typ abgeleitet von T;
  • (*t1).*f wenn N == 1 und f ein Zeiger auf Mitgliedsdaten einer Klasse T ist und t 1 keiner der im vorherigen Punkt beschriebenen Typen ist;
  • f(t1, t2, …, tN) in allen anderen Fällen.

Eine weitere allgemeine Tatsache, auf die ich hinweisen möchte, ist, dass der Thread-Konstruktor standardmäßig alle ihm übergebenen Argumente kopiert. Der Grund dafür ist, dass die Argumente möglicherweise den aufrufenden Thread überleben müssen, das Kopieren der Argumente garantiert dies. Wenn Sie stattdessen wirklich eine Referenz übergeben möchten, können Sie a verwenden std::reference_wrapper erstellt von std::ref.

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

Indem Sie dies tun, versprechen Sie, dafür zu sorgen, dass die Argumente noch vorhanden sind, wenn der Thread mit ihnen arbeitet.


Beachten Sie, dass alle oben genannten Dinge auch angewendet werden können std::async und std::bind.

  • So kompiliert es zumindest. Obwohl ich keine Ahnung habe, warum Sie die Instanz als zweites Argument übergeben.

    – Bergmeier

    20. Mai 2012 um 13:37 Uhr


  • @LCID: Die Multi-Argument-Version von std::thread Der Konstruktor funktioniert so, als ob die Argumente übergeben würden std::bind. Um eine Member-Funktion aufzurufen, ist das erste Argument to std::bind muss ein Zeiger, eine Referenz oder ein gemeinsam genutzter Zeiger auf ein Objekt des entsprechenden Typs sein.

    – David S

    20. Mai 2012 um 13:49 Uhr


  • Woher nehmen Sie es, dass der Konstruktor wie ein Implizit wirkt bind? Ich kann das nirgends finden.

    – Kerrek SB

    20. Mai 2012 um 13:58 Uhr

  • @KerrekSB, vergleiche [thread.thread.constr]p4 mit [func.bind.bind]p3, die Semantik ist ziemlich ähnlich, definiert in Bezug auf den INVOKE-Pseudocode, der definiert, wie Elementfunktionen aufgerufen werden

    – Jonathan Wakely

    20. Mai 2012 um 14:39 Uhr


  • Denken Sie daran, dass nicht statische Elementfunktionen als erster Parameter eine Instanz der Klasse annehmen (für Programmierer nicht sichtbar). Wenn Sie diese Methode also als Rohfunktion übergeben, werden Sie immer auf ein Problem während der Kompilierung und Deklarationsabweichung stoßen.

    – Zoska

    10. Oktober 2013 um 11:56 Uhr

Thread mit Member Funktion starten
RnMss

Da Sie C++11 verwenden, ist Lambda-Ausdruck eine schöne und saubere Lösung.

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

seit this-> kann weggelassen werden, es könnte verkürzt werden zu:

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

oder nur (veraltet)

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

  • Im Allgemeinen sollten Sie nicht verwenden std::move wenn eine lokale Variable nach Wert zurückgegeben wird. Dies hemmt tatsächlich RVO. Wenn Sie nur den Wert zurückgeben (ohne die Bewegung), verwendet der Compiler möglicherweise RVO, und wenn dies nicht der Fall ist, sagt der Standard, dass er die Bewegungssemantik aufrufen muss.

    – zmb

    10. Oktober 2013 um 11:53 Uhr

  • @zmb, mit der Ausnahme, dass Code auf VC10 kompiliert werden soll, müssen Sie verschieben, wenn der Rückgabetyp nicht CopyConstructable ist.

    – Bergmeier

    10. Oktober 2013 um 13:20 Uhr

  • RVO generiert immer noch besseren Code als Move-Semantik und wird nicht verschwinden.

    – Jonathan Wakely

    9. Oktober 2014 um 10:41 Uhr


  • Sei vorsichtig mit [=]. Damit können Sie versehentlich ein riesiges Objekt kopieren. Im Allgemeinen ist es ein Code-Geruch benutzen [&] oder [=].

    – rostig

    9. September 2016 um 8:37 Uhr

  • @Everyone Vergiss nicht, dass es hier ein Thread ist. Dies bedeutet, dass die Lambda-Funktion ihren Kontextbereich überdauern kann. Durch die Verwendung von Capturing-by-Reference ([&]), können Sie Fehler wie einige baumelnde Referenzen einführen. (Zum Beispiel, std::thread spawn() { int i = 10; return std::thread( [&] { std::cout<<i<<"\n"; } ); })

    – RnMss

    9. Januar 2018 um 8:13 Uhr


Hier ist ein vollständiges Beispiel

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

Das Kompilieren mit g++ führt zu folgendem Ergebnis

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)

  • nicht wirklich relevant für die OP-Frage, aber warum weisen Sie Wrapper auf dem Heap zu (und geben ihn nicht frei)? hast du java/c# hintergrund?

    – Alessandro Teruzzi

    13. Oktober 2016 um 13:34 Uhr

  • Vergiss es nicht delete die erinnerung vom haufen 🙂

    – Slack-Bot

    21. März 2020 um 21:03 Uhr

  • Es gibt keinen Grund, ein Objekt kurz vor Programmende zu löschen. Beschämen Sie Menschen nicht ohne Grund.

    – CaptainCodeman

    15. August 2020 um 23:19 Uhr

@hop5 und @RnMss schlugen vor, C++11-Lambdas zu verwenden, aber wenn Sie sich mit Zeigern befassen, können Sie sie direkt verwenden:

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

Ausgänge

2

Umgeschriebenes Beispiel aus dieser Antwort wäre dann:

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

Einige User haben bereits ihre Antwort gegeben und sehr gut erklärt.

Ich möchte noch ein paar Dinge hinzufügen, die sich auf Threads beziehen.

  1. Wie man mit Funktor und Thread arbeitet. Bitte beachten Sie das folgende Beispiel.

  2. Der Thread erstellt beim Übergeben des Objekts eine eigene Kopie des Objekts.

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

Eine andere Möglichkeit, dasselbe zu erreichen, ist wie folgt:

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

    t.join();
}

Wenn Sie das Objekt jedoch als Referenz übergeben möchten, verwenden Sie die folgende Syntax:

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

  • Hallo, könnten Sie erklären, wie es möglich ist, einen Thread aus einer Member-Funktion zu erstellen, ohne ein Objekt zu erstellen? im Fall thread t((CB())); nicht CB Objekt erstellt? Können Sie meine Frage hier beantworten, bitte stackoverflow.com/q/71152949/7264131

    – Trauriges Ei

    17. Februar um 5:56 Uhr

  • Hallo, könnten Sie erklären, wie es möglich ist, einen Thread aus einer Member-Funktion zu erstellen, ohne ein Objekt zu erstellen? im Fall thread t((CB())); nicht CB Objekt erstellt? Können Sie meine Frage hier beantworten, bitte stackoverflow.com/q/71152949/7264131

    – Trauriges Ei

    17. Februar um 5:56 Uhr

1002400cookie-checkThread mit Member-Funktion starten

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy