OVH Cloud OVH Cloud

Lecture bloquante de FIFO

15 réponses
Avatar
Nicolas HUYNH
Bonjour à tous

Existe-t-il un moyen simple d'écrire un thread en C ou C++
qui attende des éléments sur une FIFO (une queue STL par exemple)
*sans faire d'attente active* (même avec une temporisation) ?

Concrètement, je ne souhaite pas de :

while (true) {
if (!queue->empty()) {
element = queue->front();
...
queue->pop();
}
/* éventuellement une temporisation ici */
}

mais plutôt un appel bloquant "à la langage Ada"

Merci pour toute réponse !

N.

10 réponses

1 2
Avatar
DINH Viêt Hoà

Existe-t-il un moyen simple d'écrire un thread en C ou C++
qui attende des éléments sur une FIFO (une queue STL par exemple)
*sans faire d'attente active* (même avec une temporisation) ?


en C standard, cela n'existe pas, en POSIX, tu as les sémaphores.

<POSIX>
#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned value);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_destroy(sem_t *sem);
</POSIX>

Il s'agit d'un compteur de ressources disponibles bloquant lorsqu'il
arrive sur zéro. Tu peux choisir la valeur initiale du compteur.

Tu dois avoir le même genre de choses sous windows.

--
DINH V. Hoa,

"il faut arrêter de fumer et de boire de la bière. Avec les économies
réalisées, on s'achète un PC" -- ed

Avatar
Christophe Lephay
Nicolas HUYNH wrote:
Existe-t-il un moyen simple d'écrire un thread en C ou C++
qui attende des éléments sur une FIFO (une queue STL par exemple)
*sans faire d'attente active* (même avec une temporisation) ?

Concrètement, je ne souhaite pas de :

while (true) {
if (!queue->empty()) {
element = queue->front();
...
queue->pop();
}
/* éventuellement une temporisation ici */
}

mais plutôt un appel bloquant "à la langage Ada"

Merci pour toute réponse !


Il n'y a pas de méthode portable. Tu devrais reposer la question dans un
groupe dédié à la programmation dans ton environnement. La solution reposera
vraisemblablement sur un sémaphore, pour peu que ceux-ci y soient
disponibles (ou éventuellement sur des signaux ou messages, même si celà ne
sera plus vraiment "un appel bloquant à la ADA").

Chris

Avatar
Christophe de VIENNE
Nicolas HUYNH wrote:
Bonjour à tous

Existe-t-il un moyen simple d'écrire un thread en C ou C++
qui attende des éléments sur une FIFO (une queue STL par exemple)
*sans faire d'attente active* (même avec une temporisation) ?


Si ça t'interesse j'ai implémenté ça en utilisant boost/thread et les
queue de la stl.

C'est pas parfait (ça hérite d'un conteneur STL, ce qui n'est par
forcément le mieux), mais ça fonctionne plutôt bien (tout commentaire
sur le style est le bienvenue d'ailleurs).


A+

Christophe


Le code (ça tient dans un header) :

6 :~/prog/cvtools/cvtools> cat sync_queue.hpp
#ifndef _CVTOOLS_SYNC_QUEUE_H_
#define _CVTOOLS_SYNC_QUEUE_H_

#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/xtime.hpp>

#include <queue>

namespace cvtools {

template <class Queue>
struct not_empty {
Queue & q;
not_empty(Queue & q): q(q) {};
bool operator()() {
return ! q.empty();
}
};

template <class Queue>
class sync_queue: private Queue
{
public:
typedef Queue queue_type;
typedef typename Queue::value_type
value_type;
typedef typename Queue::reference
reference;
typedef typename Queue::const_reference
const_reference;
typedef typename Queue::size_type
size_type;

private:
boost::mutex my_mutex;
boost::condition my_condition;

public:
void push(const_reference value);
void pop(reference value);
bool pop(reference value, unsigned long
msec);
bool pop(reference value, const
boost::xtime & xt);

bool empty();
size_type size();
};

template<typename T, typename Sequence>
inline void my_pop(
std::queue< T, Sequence > & q,
typename std::queue< T, Sequence
::reference value)
{

value = q.front();
q.pop();
}

template<typename T, typename Sequence, typename Compare>
inline void my_pop(
std::priority_queue< T, Sequence,
Compare > & q,
typename std::priority_queue< T,
Sequence, Compare >::reference value)
{
value = q.top();
q.pop();
}

template <class Queue>
inline void sync_queue<Queue>::push(const_reference __x)
{
boost::mutex::scoped_lock lock(my_mutex);
this->Queue::push(__x);
my_condition.notify_one();
}

template <class Queue>
inline void sync_queue<Queue>::pop(reference value)
{
boost::xtime xt = {0,0};
pop(value, xt);
}

template <class Queue>
inline bool sync_queue<Queue>::pop(reference value,
unsigned long msec)
{
boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC);
if(msec != 0)
{
xt.sec += msec / 1000;
xt.nsec += (msec % 1000) * 1000000;
}
return pop(value, xt);
}

template <class Queue>
inline bool sync_queue<Queue>::pop(reference value,
const boost::xtime & xt)
{
boost::mutex::scoped_lock lock(my_mutex);
if( this->Queue::empty() )
{
if(xt.sec != 0)
{
if(
my_condition.timed_wait(lock, xt, not_empty<queue_type>(*this) ) )
{
my_pop(*this, value);
return true;
}
else
{
return false;
}
}
else
{
my_condition.wait(lock,
not_empty<queue_type>(*this));
my_pop(*this, value);
return true;
}
}
else
{
my_pop(*this, value);
return true;
}
}

template <class Queue>
inline bool sync_queue<Queue>::empty()
{
boost::mutex::scoped_lock lock(my_mutex);
return this->Queue::empty();
}

template <class Queue>
inline typename sync_queue<Queue>::size_type
sync_queue<Queue>::size()
{
boost::mutex::scoped_lock lock(my_mutex);
return this->Queue::size();
}

};

#endif

Avatar
Christophe de VIENNE
Mes excuses aux lecteurs de fclc, je n'avais pas vue le cross-post...
Avatar
Pascal
Nicolas HUYNH wrote:
Bonjour à tous

Existe-t-il un moyen simple d'écrire un thread en C ou C++
qui attende des éléments sur une FIFO (une queue STL par exemple)
*sans faire d'attente active* (même avec une temporisation) ?

Concrètement, je ne souhaite pas de :

while (true) {
if (!queue->empty()) {
element = queue->front();
...
queue->pop();
}
/* éventuellement une temporisation ici */
}

mais plutôt un appel bloquant "à la langage Ada"

Merci pour toute réponse !

N.

Tu peux utiliser la commande seletct.


--
Pascal

Avatar
DINH Viêt Hoà

Nicolas HUYNH wrote:
Bonjour à tous

Existe-t-il un moyen simple d'écrire un thread en C ou C++
qui attende des éléments sur une FIFO (une queue STL par exemple)
*sans faire d'attente active* (même avec une temporisation) ?

Concrètement, je ne souhaite pas de :

while (true) {
if (!queue->empty()) {
element = queue->front();
...
queue->pop();
}
/* éventuellement une temporisation ici */
}

mais plutôt un appel bloquant "à la langage Ada"

Merci pour toute réponse !

Tu peux utiliser la commande seletct.



on doit préciser que cette fonction n'exite qu'en POSIX,
Enfin son utilisation n'est pas trop adaptée à la situation.

--
DINH V. Hoa,

"il faut arrêter de fumer et de boire de la bière. Avec les économies
réalisées, on s'achète un PC" -- ed


Avatar
Emmanuel Delahaye
In 'fr.comp.lang.c', Nicolas HUYNH wrote:

Existe-t-il un moyen simple d'écrire un thread en C ou C++


Il n'y a pas de thread en C. C'est une ressource système. Merci de reposter
sur un forum dédié à ton système.

--
-ed- [remove YOURBRA before answering me]
The C-language FAQ: http://www.eskimo.com/~scs/C-faq/top.html
C-reference: http://www.dinkumware.com/manuals/reader.aspx?lib=cpp
FAQ de f.c.l.c : http://www.isty-info.uvsq.fr/~rumeau/fclc/

Avatar
M. B.
"DINH Viêt Hoà" a écrit dans le message news:


"il faut arrêter de fumer et de boire de la bière. Avec les économies
réalisées, on s'achète un PC" -- ed



Non, il faut continuer, sinon on va tous devenir fou a force
de vivre dans un monde numerique et beaucoup trop logique.

MB

Avatar
Bertrand Motuelle
"Christophe de VIENNE" schrieb im Newsbeitrag
news:newscache$qvyxnh$n2c$
Nicolas HUYNH wrote:
Bonjour à tous

Existe-t-il un moyen simple d'écrire un thread en C ou C++
qui attende des éléments sur une FIFO (une queue STL par exemple)
*sans faire d'attente active* (même avec une temporisation) ?


Si ça t'interesse j'ai implémenté ça en utilisant boost/thread et les
queue de la stl.

C'est pas parfait (ça hérite d'un conteneur STL, ce qui n'est par
forcément le mieux), mais ça fonctionne plutôt bien (tout commentaire
sur le style est le bienvenue d'ailleurs).

#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/xtime.hpp>

#include <queue>


Comme tu le dis, l'héritage privé dans ce cas n'est pas génial (je trouve
qu'ici ca ne fait que compliquer la lecture du code).

namespace cvtools {

template <class Queue>
struct not_empty {
Queue & q;
not_empty(Queue & q): q(q) {};
bool operator()() {
return ! q.empty();
}
};
Pourquoi ne pas utiliser les const ci-dessus ?


template <class Queue>
class sync_queue: private Queue
{
public:
typedef Queue queue_type;
typedef typename Queue::value_type
value_type;
typedef typename Queue::reference
reference;
typedef typename Queue::const_reference
const_reference;
typedef typename Queue::size_type
size_type;

private:
boost::mutex my_mutex;
boost::condition my_condition;

public:
void push(const_reference value);
void pop(reference value);
bool pop(reference value, unsigned long
msec);
bool pop(reference value, const
boost::xtime & xt);

bool empty();
size_type size();
};
Personellement, j'aurais plutôt tendance à mettre les fonctions publiques

avant la section privée.
Je déclarerais empty() et size() const, et my_mutex et my_condition mutable.
Et puis, ben je supprimerais l'héritage et déclarerais my_queue ;-)


template<typename T, typename Sequence>
inline void my_pop(
std::queue< T, Sequence > & q,
typename std::queue< T, Sequence
::reference value)
{

value = q.front();
q.pop();
}


template<typename T, typename Sequence, typename Compare>
inline void my_pop(
std::priority_queue< T, Sequence,
Compare > & q,
typename std::priority_queue< T,
Sequence, Compare >::reference value)
{
value = q.top();
q.pop();
}
C'est marrant, j'ai l'impression d'avoir dejà vu ca dans un de tes messages

ici, il y a quelques mois :-)

template <class Queue>
inline void sync_queue<Queue>::push(const_reference __x)
{
{

boost::mutex::scoped_lock lock(my_mutex);
this->Queue::push(__x);
}

my_condition.notify_one();
C'est pinailler, mais le mutex peut être relaché avant l'appel notify_one()

(dans le cas général).

}


[snip surcharges de pop()]

Puisque tu demandais des commentaires sur le style: l'idiome avec les
variables conditionnelles est plutôt d'utiliser un while sur la condition
(les threads bloqués peuvent souffrir de "spurious wakeup").
Je trouves que la solution que tu as retenue (le wait + prédicat, qui cache
le while) est beaucoup moins lisible (combinée avec l'héritage privé, il m'a
fallu quelque retours arrières + avances rapides avant de voir quelle
fonction empty() était réelement appelée).

Perso, je trouve ceci bien plus simple:

template <class Queue>
inline bool sync_queue<Queue>::pop(reference value)
{
boost::mutex::scoped_lock lock(my_mutex);
while( this->Queue::empty() ) // ou encore mieux, my_queue.empty()
{
my_condition.wait(lock);
}
my_pop(*this, value);
return true;
}

Enfin, je n'aurais pas essayé de coller les deux wait/timed_wait dans la
même fonction pop.
Je trouve que garder deux fonctions séparées n'a que des avantages
(lisibilité, pas de création de variable inutile dans le cas du wait...)

[snip empty(), size()]

Et sinon, je ne peux qu'être d'accord avec ton implémentation: mis à part 2
ou 3 détails c'est vraiment comme ca que je fais :-)
(mais sans boost, un de mes compilos y est allergique).

Bertrand.


Avatar
Benoit Rousseau
M. B. wrote:
"DINH Viêt Hoà" a écrit dans le message news:


"il faut arrêter de fumer et de boire de la bière. Avec les économies
réalisées, on s'achète un PC" -- ed




Non, il faut continuer, sinon on va tous devenir fou a force
de vivre dans un monde numerique et beaucoup trop logique.



Et a 1,50 Euro la bierre en Belgique, il va falloir se priver longtemps
pour s'acheter un PC...


--
--------------------------------------------
Benoît Rousseau : roussebe at spray dot se
Jouez en programmant : http://realtimebattle.sourceforge.net/


1 2