J'infirme. A doit prendre le mutex pour rajouter un message. Et ce mutex est celui... de la condition variable. Autrement dit, libérer le mutex et s'endormir est une opération atomique.
Version imprimable
J'infirme. A doit prendre le mutex pour rajouter un message. Et ce mutex est celui... de la condition variable. Autrement dit, libérer le mutex et s'endormir est une opération atomique.
Genial !
Ben ecoute je pense avoir compris comment faire !
Je vais tester ca et je te dirais ce que ca donne !
Merci beaucoup pour ta patience :ccool:
Par défaut, non, elle n'est pas atomique. Du moins, elle n'est pas garantie comme étant atomique, ce qu'il faut comprendre comme "merde infâme à déplanter et qui ne plantera QUE sur les machines des clients, sur lesquelles tu n'as aucun outil de debug". Car bien sûr, ça ne plantera jamais sur les machines de développement ou de validation...
Règle d'or de la programmation parallèle : N'est atomique que ce qui est explicitement décrit comme étant atomique. Sinon, considère que ça peut foirer, et en général, c'est le cas. Tu as raison sur un point : dans 99.9999...% des cas, la copie d'un mot machine est effectivement atomique. Mais tu as toujours des cas vicieux où cela ne sera pas le cas, surtout si ton instruction utilise plusieurs cycles CPU.
Par contre, en fonction de l'architecture matérielle, tu as des opérations simples pouvant être réellement atomiques. Sur Windows, ce sont les fonctions Interlocked* : pour faire court, ce sont des encapsulations d'une instruction CPU x86 simple (et atomique dans un contexte monotâche) rendue atomique de force via un préfixe lock, qui garantit que les emplacements mémoire visés seront bloqués pendant l'exécution de l'instruction... D'où le fait que la TBB implémente une FIFO exploitant ce mécanisme, d'ailleurs, vu que c'est d'abord et avant tout une fonctionnalité des processeurs x86.
J'avoue d'ailleurs que j'ai toujours autant de mal à comprendre pourquoi ces fonctions n'existent pas par défaut sur Linux, quitte à les remplacer par une version "bourrine" à base de mutex si l'architecture matérielle n'offre pas le support adéquat. Au moins, sur les CPU Intel, elles seraient performantes, et même sur une archi différente, on évite un passage user / kernel "pour rien", ce qui reste intéressant par rapport à une solution manuelle.
Ok je viens de faire mon implementation !
Ca fonctionne parfaitement c'est terrible :mouarf:
0% du CPU a vide et un serveur plus reactif qu'avec des sleeps je me suis pas fais chier pour rien.
Merci a tous pour votre aide !
La class que j'ai fais grace a votre aide (Les class Mutex et Condition sont des abstractions des criticals sections et conditions) :
Voila Merci :ccool:Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 template <typename Type> class WaitSafeQueue { private: typedef std::queue<Type> QUEUE; QUEUE _queue; QUEUE _consumer_queue; Mutex _mutex; Condition _condition; public: WaitSafeQueue() : _condition(_mutex) {}; virtual ~WaitSafeQueue() {}; virtual Type pop() { if (_consumer_queue.empty() == false) { Type val = this->_consumer_queue.front(); this->_consumer_queue.pop(); return (val); } else { this->_mutex.lock(); if (this->_queue.empty() == true) { this->_condition.sleep(); } Type val = this->_queue.front(); this->_queue.pop(); while (this->_queue.empty() == false) { this->_consumer_queue.push(this->_queue.front()); this->_queue.pop(); } this->_mutex.unlock(); return (val); } } virtual bool empty() { return (this->_queue.empty() && this->_consumer_queue.empty()); } virtual size_t size() { return (this->_queue.size() + this->_consumer_queue.size()); } virtual void push(Type val) { this->_mutex.lock(); this->_queue.push(val); this->_mutex.unlock(); this->_condition.wake(); } };