thread und rvalues



  • Ich habe eine only-movable Klasse und möchte eine Instanz derer an einen std::thread-Konstruktor übergeben. Momentan ist in der Implementierung von Microsoft noch ein Bug der das leider unmöglich macht. (http://connect.microsoft.com/VisualStudio/feedback/details/737812)

    Läuft schief:

    void myfunc(myClass&&);
    
    myClass instance;
    std::thread(&myfunc, std::move(instance)).detach();
    

    Ohne das explizite std::move geht es zwar, aber kopieren ist ja verboten, also meckert der Compiler. Ich bin echt verzweifelt, kennt da jemand einen Hack oder so?



  • Der Hack ist: lass das && in der Funktionssignatur weg. Wenn deine Klasse einen Move-Konstruktor hat, wird es dann auch kompilieren (und lies ggf. noch mal etwas über die Move-Semantik).



  • Nein, das funktioniert leider nicht. Der Compiler sagt trotzdem dass versucht wurde auf eine gelöschte Funktion zu verweisen (der Kopierkonstruktor). Ich weiß auch nicht wieso genau das nicht funktioniert, MS hat da halt irgendwas sehr komisch implementiert.



  • Kannst Du Dir nicht einen passenden Kopierkonstruktor schreiben?



  • Ich kann bestätigen, dass VS2013 keine Objekte in Threads moven lässt, weder mit std::thread noch mit std::async. Auf der verlinkten Seite ist ein Workaround angegeben:

    void myfunc(std::reference_wrapper<myClass>);
    
    myClass instance;
    std::thread(&myfunc, std::ref(instance)).detach();
    

    Mir ist aber nicht klar, was das für Auswirkungen hat.



  • Normale Referenz übergeben und dann im Thread moven.

    help_fuck_ms schrieb:

    MS hat da halt irgendwas sehr komisch implementiert.

    Wohl kaum, move-only-Typen in einen Thread zu bekommen ist verdammt kompliziert, habe ich jedenfalls beim Versuch einer std::thread-Implementation nicht hinbekommen. Wenn man darauf nicht achtet kann man das hingegen mit einem simplen std::bind lösen.



  • qqq schrieb:

    Normale Referenz übergeben und dann im Thread moven.

    Du willst ja sowas machen können:

    void foo(std::vector<T> v){
        thread(bla, std::move(v)).detach();
    }
    

    Bei Referenz übergeben und im Thread moven gibt es den vector vielleicht gar nicht mehr. Also muss man warten, bis der Thread mit dem moven fertig ist und muss Threads synchronisieren, was es zu vermeiden gilt. Man kann den vector einem std::auto_ptr anvertrauen oder etwas was anders heißt und dasselbe tut um den Aufschrei zu vermeiden. Man kann auch "sicher gehen" indem man einen std::shared_ptr mitsamt des Overheads in Kauf nimmt. Alles nicht so richtig schön.



  • Also einfach einen shared_ptr auf ein struct thread_arguments oder sowas zu übergeben scheint mir doch die sinnvollste Lösung zu sein diesen VS Bug zu umgehen (der liegt btw nicht unbedingt an der Library, sondern daran dass VS einfach move immer noch nicht vollständig implementiert hat), der Overhead von shared_ptr gegenüber den Kosten einen Thread zu starten ist eh vernachlässigbar.



  • Die schreiben doch selbst dass es an der Library liegt -- weil sie halt std::bind verwenden, was für den Thread-Ctor aber die falsche Semantik hat.



  • nwp3 schrieb:

    Du willst ja sowas machen können:

    void foo(std::vector<T> v){
        thread(bla, std::move(v)).detach();
    }
    

    Bei Referenz übergeben und im Thread moven gibt es den vector vielleicht gar nicht mehr. Also muss man warten, bis der Thread mit dem moven fertig ist und muss Threads synchronisieren, was es zu vermeiden gilt.

    Das Problem gibt es aber nur wenn man es mit std::ref macht, nen? Der obige Code mit std::move sollte mMn. ohne Synchroniserung korrekt funktionieren, da das Objekt v ja erstmal in das Thread-Objekt reingemoved wird, und danach erst der Thread gestartet.



  • hustbaer schrieb:

    Der obige Code mit std::move sollte mMn. ohne Synchroniserung korrekt funktionieren, da das Objekt v ja erstmal in das Thread-Objekt reingemoved wird, und danach erst der Thread gestartet.

    Wenn es kompilieren würde sollte es so funktionieren. Tut es aber nicht in VisualStudio. Der Workaround soll aber genauso gut funktionieren. Ich glaube std::ref hat hier keinerlei Vorteil gegenüber einer normalen Referenz und das ist schlecht. Mein Favorit ist immernoch auto_ptr.



  • Jo.

    Und... das "in das thread Objekt reingemoved" von mir ist natürlich Unsinn.

    Sonst gäbe es ja erst wieder ein Problem wenn das thread Objekt zu früh zerstört wird. std::thread muss intern wohl den Funktor den der Thread dann ausführen soll "dynamisch instanzieren", und dann Ownership an die Trampolin-Funktion des Thread transferieren.
    Die zu bindenden Parameter müssen in diesen dynamisch instanzierten Funktor reingemoved werden, nicht in das std::thread Objekt. Aber halt innerhalb des Konstruktors von std::thread .


Log in to reply