2016-07-27 2 views
0

J'essaie de développer avec ce que certaines personnes m'ont aidé ici Call function inside a lambda passed to a thread afin que ma classe ouvrière puisse supporter un constructeur de mouvement et un mouvement operator=, mais j'ai le problème que ma classe lie this par copie (ou référence) au thread afin qu'il puisse accéder aux valeurs de classe. Lesquels sont plusieurs atomic<bool>, un condition_variable et un mutex.Déplacer des opérations pour une classe avec un thread comme variable membre

Mais quand j'essaie de le déplacer puisque le thread est lié à l'autre variable de condition, mutex et atomic s, tout ce que je fais dessus ne fonctionne pas. Comment puis-je réparer cela? Ai-je besoin d'utiliser un objet plus complexe et de le déplacer au lieu d'un lambda pour que le fil puisse avoir une référence? ou y at-il une autre alternative. Comme toujours l'aide sera vraiment appréciée :).

Voici un extrait (MWE) de l'implémentation.

class worker { 
public: 
    template <class Fn, class... Args> 
    explicit worker(Fn func, Args... args) { 
     t = std::thread(
      [&func, this](Args... cargs) -> void { 
       std::unique_lock<std::mutex> lock(mtx); 
       while (true) { 
        cond.wait(lock, [&]() -> bool { return ready; }); 

        if (terminate) 
         break; 

        func(cargs...); 

        ready = false; 
       } 
      }, 
      std::move(args)...); 
    } 

    worker(worker &&w) : t(std::move(w.t)) { /* here there is trouble */ } 

    worker &operator=(worker &&w) { 
     t = std::move(w.t); 
     terminate.store(wt.terminate); 
     ready.store(wt.ready); 
     return *this; 
     /* here too */ 
    } 

    ~worker() { 
     terminate = true; 
     if (t.joinable()) { 
      run_once(); 
      t.join(); 
     } 
    } 

    worker() {} 

    void run_once() { 
     std::unique_lock<std::mutex> lock(mtx); 
     ready = true; 
     cond.notify_one(); 
    } 

bool done() { return !ready; } 

private: 
    std::thread t; 
    std::atomic<bool> ready, terminate; // What can I do with all these? 
    std::mutex mtx;      // 
    std::condition_variable cond;  // 
}; 

int main() { 
    worker t; 
    t = worker([]() -> void { cout << "Woof" << endl; }); 
    t.run_once(); 
    while(!t.done()) ; 
    return 0; 
} 

Désolé pour le gros dump de code.

+0

Votre problème est que vous déplacez un objet qui est utilisé dans un thread d'en dessous dans un autre. Question: pourquoi? Créez l'objet dynamiquement et utilisez des pointeurs intelligents. – kfsone

+0

@kfsone Y-ouais c'était ce que j'allais faire avec ma deuxième question, je voulais savoir s'il y avait quelque chose de plus élégant ou qui me manquait. – Aram

Répondre

2

Je « réparer » par dire simplement worker est noncopyable et nonmoveable et laisse à l'utilisateur de stocker worker comme un unique_ptr si elles veulent déplacer. Il n'y a rien de mal à ça du tout. C'est ordinaire, en fait.

Si vous voulez absolument que cette classe soit mobile, vous pouvez utiliser le motif de conception Pimpl: créer une classe imbriquée Worker::Impl dont worker est propriétaire par unique_ptr. La classe Impl serait non-oculaire et non-mobile et serait votre classe actuelle worker. Le lambda aurait un pointeur vers la classe Impl, pas la classe worker. La classe worker ne contiendrait rien sauf un unique_ptr au Impl et les fonctions qui transmettent aux fonctions de la classe Impl, et les opérateurs/copieurs de copie et de déplacement par défaut fonctionneront correctement pour les deux classes (le travail sera copiable mais non mobile, impl être non recouvrable et non déplaçable).

+0

Ok, je vais probablement faire le second. Merci. – Aram

+0

@Aram Je le recommande fortement, je l'ai seulement mis là pour l'exhaustivité - pour montrer que c'est techniquement possible. La façon moderne de «réparer» ce problème est de ne rien faire. Avoir une classe non-oculaire/non-amovible et laisser l'utilisateur de la classe le gérer. – David

+0

quels sont les inconvénients de cette approche si je pouvais demander? – Aram