Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

stringstream + threads

18 réponses
Avatar
Mickybadia
Bonjour les amis,

J'ai un stringstream défini globalament, et ensuite un programme
double-thread, l'un (W) qui écrit dedans et l'autre (R) qui lit. En
gros, W tourne en boucle et filtre std::in pour R, le thread principal
(qui a créé W) et qui lit l'entrée filtrée dans le stringstream.

...et ça ne marche pas : W écrit bien dans le stream mais les getline
dans R ne retournent que des lignes vides. Une explication?

Merci.




Résumé du code pertinent :

#include <sstream>
static std::stringstream fcin; // Filtered cin

class IOThread : public QThread {
public:
void run() { // définit W
string ln;
while (getline(cin, ln)) {
if (filter(ln))
fcin << ln << endl;
}
cout << "Reached end of input, IO exits." << endl;
}
};

class IO { //thread créé et W lancé dans le constructeur
public:
string readln() {
string line;
getline(fcin, line);
trace("(input) " + line);
return line;
}
}

// puis dans le code, un objet IO créé

--
Micky

10 réponses

1 2
Avatar
Michael Doubez
On 15 juil, 04:19, Mickybadia wrote:
J'ai un stringstream défini globalament, et ensuite un programme
double-thread, l'un (W) qui écrit dedans et l'autre (R) qui lit. En
gros, W tourne en boucle et filtre std::in pour R, le thread principal
(qui a créé W) et qui lit l'entrée filtrée dans le stringstream.



Je suppose que le stringstream est protégé par un mutex.

...et ça ne marche pas : W écrit bien dans le stream mais les getline
dans R ne retournent que des lignes vides. Une explication?



As tu vérifié l'état de ta stream ?
Je dirais que le eof bit est levé et tu ne détecte pas l'erreur car tu
ne le vérifie dans IO::readln().

--
Michael
Avatar
James Kanze
On Jul 15, 8:32 am, Michael Doubez wrote:
On 15 juil, 04:19, Mickybadia wrote:



> J'ai un stringstream défini globalament, et ensuite un programme
> double-thread, l'un (W) qui écrit dedans et l'autre (R) qui lit. En
> gros, W tourne en boucle et filtre std::in pour R, le thread principal
> (qui a créé W) et qui lit l'entrée filtrée dans le stringstream .



Je suppose que le stringstream est protégé par un mutex.



Je n'en ai pas vu dans son code, et je ne vois pas l'intérêt de
le faire dans le stringstream même.

> ...et ça ne marche pas : W écrit bien dans le stream mais
> les getline dans R ne retournent que des lignes vides. Une
> explication?



As tu vérifié l'état de ta stream ?
Je dirais que le eof bit est levé et tu ne détecte pas
l'erreur car tu ne le vérifie dans IO::readln().



Plus généralement, stringstream ne peut pas servir comme il
semble vouloir. Il n'est pas conçu pour ça : une lecture d'un
stringstream n'attend jamais ; s'il n'y a rien dedans, il
retourne tout de suite avec fin de fichier. Pour synchroniser et
communiquer entre deux threads, il faut utiliser les moyens qui
conviennent : un mutex et une condition sous Unix (ou avec
boost::threads -- mais il a l'air d'utiliser QThread, que je ne
connais pas), par exemple. (Et même avec ces moyens, je doute
que stringstream puisse servir. Puisqu'il n'écrit et ne lit que
des lignes, une std::deque< std::string > me semble tout
indiqué.)

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
Mickybadia
> Plus généralement, stringstream ne peut pas servir comme il
semble vouloir.



OK. En effet les stringstreams étant des iostreams je pensais que ça
ferait pareil, lecture blocante etc. Merci de me l'indiquer. Au passage,
je trouve alors bizarre cet héritage. On n'est pas loin de ne tenir plus
que par le nom des méthodes et plus par les concepts modélisés par les
classes.

une std::deque< std::string > me semble tout
indiqué.)



C'est en effet une solution, juste qu'il me faut alors implanter la
lecture blocante moi-même. Un pipe à la limite...

OK merci. :)
Avatar
Mickybadia
>> J'ai un stringstream défini globalament, et ensuite un programme
double-thread, l'un (W) qui écrit dedans et l'autre (R) qui lit. En
gros, W tourne en boucle et filtre std::in pour R, le thread principal
(qui a créé W) et qui lit l'entrée filtrée dans le stringstream.



Je suppose que le stringstream est protégé par un mutex.



?!
Comprends pas. Au pire si yen avait un on ne ferait qu'attendre qu'il se
débloque non ?
Avatar
Michael Doubez
On 15 juil, 15:50, Mickybadia wrote:
>> J'ai un stringstream défini globalament, et ensuite un programme
>> double-thread, l'un (W) qui écrit dedans et l'autre (R) qui lit. En
>> gros, W tourne en boucle et filtre std::in pour R, le thread principal
>> (qui a créé W) et qui lit l'entrée filtrée dans le stringstrea m.

> Je suppose que le stringstream est protégé par un mutex.

?!
Comprends pas. Au pire si yen avait un on ne ferait qu'attendre qu'il se
débloque non ?



Stringstream ne fournit aucunes garanties de multithreading. Si tu
veux l'utiliser, il faut la protéger par un mutex.

Mais comme la indiqué James Kanze, si ton but est de faire
l'équivalent d'un pipe alors stringstream n'est pas ce qu'il te faut.

--
Michael
Avatar
Mickybadia
Je reviens ici car il demeure une chose inexpliquée...

J'ai donc mutexé un deque<string> et implanté la lecture blocante (avec
une attente active pour l'instant – ne criez pas). Mais il y a autre
chose que je n'explique pas, la même en réalité que celle qui a lancé
mon 1er post. Le problème était déjà visible même avec les strinstreams
mal choisis, et toujours pas résolu : le lecteur ne voit pas les modifs
faites par l'écrivain. POURQUOI ??



Petit update du snippet donné avant ci-dessous. Encore une fois, on
boucle sur un affichage de "got in, size here is 0". En traçant l'exec
du thread créé on a tout bon par contre.

-----
static std::deque<string> fcin; // Filtered cin
static QMutex fcinAccess;

class IOThread : public QThread {
public:
void run() {
string ln;
while (getline(cin, ln)) {
if (filter(ln)) {
fcinAccess.lock();
fcin.push_back(ln);
cerr << "PUSHED a line in fcin queue (now size "
<< fcin.size() << "): " << ln << endl;
fcinAccess.unlock();
}
}
cout << "Reached end of input, IO exits." << endl;
}
};

class IO {
public:
IO() {
traceFlag = true;
io = new IOThread;
io->start();
}

private:
IOThread *io;
bool traceFlag;

public:
string readln() {
string line;
bool gotLine = false;
while (!gotLine) {
fcinAccess.lock();
cerr << "got in, size here is " << fcin.size() << endl;
if (!fcin.empty()) {
line = fcin.front();
fcin.pop_front();
gotLine = true;
}
fcinAccess.unlock();
}
trace("(input) " + line);
return line;
}
};


--
Mickybadia
Avatar
Mickaël Wolff
Mickybadia a écrit :
J'ai donc mutexé un deque<string> et implanté la lecture blocante (avec
une attente active pour l'instant – ne criez pas). Mais il y a autre
chose que je n'explique pas, la même en réalité que celle qui a lancé
mon 1er post. Le problème était déjà visible même avec les strinstreams
mal choisis, et toujours pas résolu : le lecteur ne voit pas les modifs
faites par l'écrivain. POURQUOI ??



std::cin n'est pas interactif. std::cin attends des données sur
l'entrée standard, qui lui est fournie par le terminal. Le terminal est
le logiciel qui gère cette interaction. Pour qu'il affiche quoique ce
soit, il faut qu'il affiche de lui-même ce que tu saisis.

Le comportement dépend de ce gestionnaire de terminal, et la manière
dont tu le pilote. Il n'existe pas de méthode normalisée pour gérer le
terminal. C'est pour ça que les programmes à base de std::cin et scanf
sont bogués de base, car ils partent du postulat que std::cin et
std::cout sont destinés à l'interaction avec un utilisateur.

Bon, maintenant, quel est le but de ton programme ? Dans quel
environnement ?
--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org

Seeking for a position <http://lupusmic.org/pro/>
Avatar
Mickybadia
QUOI ??!! La plus inattendue des réponses je dois bien dire. :)

Je n'ai pas d'interactions avec un terminal. C'est en fait une interface
graphique (classes Q* dans le code trahissaient QT) qui est lancée par
un autre programme. Ce prgm fournit des lignes de texte sur ce qui est
std::in dans ce que j'ai posté, dont certaines qui ont un rôle de signal
et qui sont à traiter séparément de l'interaction principale.

J'ai donc un thread indépendant qui lit toutes les lignes sur cin (peu
importe qui l'alimente finalement), qui redirige les pseudo-signaux et
qui enfile le reste des lignes dans fcin. fcin est donc l'entrée
"filtrée" sur laquelle on lit ce qui relève de l'interaction principale.

Merci de me faire clarifier, même si je crois que d'autres avaient compris.

J'ai maintenant progressé dans mon diagnostic&debug, mais ça m'a soulevé
de nouvelles questions. J'essaie de les poster demain, dans un autre fil
(thread!) car on n'est plus dans les stringstreams maintenant :)

À plus tous. Merci d'avoir lu.
Avatar
James Kanze
On Jul 15, 3:48 pm, Mickybadia wrote:
> Plus généralement, stringstream ne peut pas servir comme il
> semble vouloir.



OK. En effet les stringstreams étant des iostreams je pensais
que ça ferait pareil, lecture blocante etc.



Ils sont comme tous les autres iostream. S'il n'y a rien dans le
fichier, ifstream revient avec EOF, sans bloquer. La différence,
c'est seulement que les fstream démandent au système s'il y a
quelque chose, et pour certains types de « fichier » (clavier,
etc.), le système prend son temps à repondre.

Merci de me l'indiquer. Au passage, je trouve alors bizarre
cet héritage. On n'est pas loin de ne tenir plus que par le
nom des méthodes et plus par les concepts modélisés par les
classes.



Au contraire. L'abstraction est rigueureusement identique.

> une std::deque< std::string > me semble tout indiqué.)



C'est en effet une solution, juste qu'il me faut alors
implanter la lecture blocante moi-même. Un pipe à la limite...



Une pipe pourrait servir, mais l'utilisation d'un std::deque
avec un mutex et une condition est beaucoup plus simple. Et
beaucoup plus souple, dans l'ensemble : l'utilisation d'une pipe
impose la sérialisation (pas un problème si les données sont des
lignes de texte, mais potentiellement un problème autrement), ce
qui enlève beaucoup d'intérêt des threads -- s'il faut
sérialiser, autant utiliser des processus.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
James Kanze
On Jul 16, 12:24 am, Mickybadia wrote:
Je reviens ici car il demeure une chose inexpliquée...



J'ai donc mutexé un deque<string> et implanté la lecture
blocante (avec une attente active pour l'instant – ne criez
pas). Mais il y a autre chose que je n'explique pas, la même
en réalité que celle qui a lancé mon 1er post. Le problème
était déjà visible même avec les strinstreams mal choisis, et
toujours pas résolu : le lecteur ne voit pas les modifs faites
par l'écrivain. POURQUOI ??



Petit update du snippet donné avant ci-dessous. Encore une
fois, on boucle sur un affichage de "got in, size here is 0".
En traçant l'exec du thread créé on a tout bon par contre.



-----
static std::deque<string> fcin; // Filtered cin
static QMutex fcinAccess;

class IOThread : public QThread {
public:
void run() {
string ln;
while (getline(cin, ln)) {
if (filter(ln)) {
fcinAccess.lock();
fcin.push_back(ln);
cerr << "PUSHED a line in fcin queue (now size "
<< fcin.size() << "): " << ln << endl;
fcinAccess.unlock();
}
}
cout << "Reached end of input, IO exits." << endl;
}
};



class IO {
public:
IO() {
traceFlag = true;
io = new IOThread;
io->start();
}



private:
IOThread *io;
bool traceFlag;



public:
string readln() {
string line;
bool gotLine = false;
while (!gotLine) {
fcinAccess.lock();
cerr << "got in, size here is " << fcin.size() << endl;
if (!fcin.empty()) {
line = fcin.front();
fcin.pop_front();
gotLine = true;
}
fcinAccess.unlock();
}
trace("(input) " + line);
return line;
}
};



Il n'y a pas assez d'information pour dire. Quand et où, par
exemple, est-ce que IO::readln est appelée ?

Plus généralement : ce que tu cherches à faire, c'est une queue
de messages. Il y a une technique standard pour le faire, avec
des conditions. (QWaitCondition, selon la documentation de QT,
qui semble utilise le même modèle, à peu de chose près, que
Posix, Boost et la prochaine norme C++, qui prendre en compte
les threads.) Alors, pourquoi pas quelque chose du genre :

class MessageQueue
{
public:
void send(
std::string const& in )
{
QMutexLocker lock( &myMutex ) ;
myData.push_back( in ) ;
myCondition.wakeOne() ;
}

std::string receive()
{
QMutexLocker lock( &myMutex ) ;
while ( myData.empty() ) {
myCondition.wait( &myMutex ) ;
}
std::string result = myData.front() ;
myData.pop_front() ;
return result ;
}

private:
std::deque< std::string >
myData ;
QMutex myMutex ;
QWaitCondition myCondition ;
} ;

Alors, les deux threads partagent une instance de MessageQueue ;
celui qui a les données appellent send, et celui qui les veut
appelle receive. (Plutôt qu'un static, je passerais une
référence à l'objet au constructeur du deuxième thread.)

En passant, aussi : je suggère que tu te formes un peu plus sur
les threading avant de s'y plonger. Il comporte pas mal de
pièges, et il faut le comprendre pour les tous éviter. (Donc,
par exemple, il faudrait prévoir quelque chose pour la
termination propre du deuxième thread. Ce qui n'est pas toujours
simple.)

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
1 2