2017-07-31 8 views
0

Tenir compte de la fonction suivante d'un travail vol sans blocage (de) Que:Force de copie (puis détruire) sur le mouvement-seul type

template<class T> 
inline T WorkStealQ<T>::Steal(void) 
{ 
    auto tail{_tail.load(std::memory_order_acquire)}; 
    if (_head.load(std::memory_order_acquire) <= tail) return T(); 
    auto task{_tasks[tail & _mask]}; 
    if (_tail.compare_exchange_weak(tail, tail + 1, std::memory_order_release, std::memory_order_relaxed)) return task; 
    return T(); 
} 

Mais si T est seulement mobile mais non copiable? Le problème est que la lecture de l'élément à partir du tampon est une opération de copie et ne peut pas être modifiée en auto task{std::move(_tasks[tail & _mask])}; car une autre opération simultanée pourrait également la déplacer, auquel cas tout constructeur de déplacement qui n'est pas en lecture seule modifie également l'original (comme annuler un pointeur sur une ressource) casserait l'algorithme.

Notez que la sémantique globale de Steal() ne réalisent qu'un mouvement d'un point de vue externe, étant donné qu'une seule opération simultanée sera de retour avec le T qui a été stocké à cet endroit; tous les autres qui perdent la course échoueront compare_exchange_weak(). Ainsi, l'opération ne casse pas la sémantique d'un mobile seulement T en ce qui concerne l'utilisateur. Malheureusement, en interne, il doit faire une copie superficielle temporaire de T jusqu'à ce qu'il détermine s'il faut le compléter comme un mouvement ou abandonner, le laissant dans le tampon (c'est fondamentalement un mouvement en deux phases avec une vérification se produisant au milieu).

Une façon de le faire serait de faire un constructeur de copie et de copier des membres privés d'affectation de T et d'avoir un friend WorkStealQ. Le problème est ce qu'il faut faire dans le cas de classes de bibliothèques tierces que je souhaite utiliser comme T. Y a-t-il une autre option dans ce cas que d'utiliser simplement des pointeurs sur de tels objets plutôt que de les stocker de manière intrusive (et ainsi obtenir un hit de performance)? Je suppose que memcpy ne fonctionnera pas même pour une copie superficielle dans le cas de classes avec des fonctions virtuelles.

+0

Ce sont des moments comme celui-ci que je pense au conseil de mon vieux vieux Tony; la première règle de la programmation sans verrou? Ne pas. – Yakk

Répondre

1

Je pense que votre meilleur pari pourrait être d'avoir une classe shallow_copy distincte qui est définie explicitement pour chaque T, et l'utiliser dans votre fonction de vol de travail. Cela vous permettra également de faire face aux problèmes de déplacement de votre type (car vous ne pouvez pas savoir a priori qu'une copie temporaire évitera ces problèmes).

+0

Voulez-vous dire shallow_copy dérivé de T? Cela fonctionnerait si les membres de T sont protégés plutôt que privés. Je devrais spécialiser les fonctions de WorkStealQ sur shallow_copy vs juste T puisque l'utilisation de shallow_copy sur quelque chose comme un entier ou un pointeur serait un surcoût inutile. –

+0

Dériver de T peut ne pas être une si bonne idée si elle ne possède pas tout ce que T fait. Si les membres de T sont protégés ou privés, et qu'il ne peut pas être copié, il me semble que cela signifie essentiellement qu'il est impossible de dire ce qui se passe pendant le déplacement, et le concept d'un déménagement en deux parties peut très bien briser les classe, pour tout ce que vous savez. Il se peut cependant que je me méprenne sur l'algorithme. – user1837296