Partage
  • Partager sur Facebook
  • Partager sur Twitter

multithreading ecriture dans un fichier

18 juin 2019 à 17:14:16

Salut !
Alors voilà, je voudrai multithreadé l'écriture dans un fichier. Vu que j'aimerai écrire des lignes, j'aimerai locker l'écriture d'une ligne (pour pas que 2 lignes se chevauchent.) par exemple, j'aimerai que mon programme écrive des lignes genre

x1, y1, z1, r1
x2, y2, z2, r2
x3, y3, z3, r3

et pas

x1, x2, y2, x3 y1 z1 y3 r1
z2, y3, r2
z3, r3 

 quand tous les threads écrivent en même temps.
Alors j'ai fais une classe Writer :

SyncWrite.hpp:

class SyncWrite
{
public:
	SyncWrite(std::string& filename);
	~SyncWrite();

	void open();
	void close();
	void LockedWrite(std::string& line);


private:
	std::string m_wFilename;
	std::ofstream m_wStream;
	boost::shared_mutex _access;


	void createFile();

};

SyncWrite.cpp

SyncWrite::SyncWrite(std::string& filename) : m_wFilename(filename)
{
	createFile();
	open();
}


SyncWrite::~SyncWrite()
{
	m_wStream.close();
}

void SyncWrite::open()
{
	m_wStream.open(m_wFilename, std::ios::out | std::ios::app);
}

void SyncWrite::close()
{
	m_wStream.close();
}

void SyncWrite::LockedWrite(std::string & line){
	boost::upgrade_lock<boost::shared_mutex> lock(_access);
	m_wStream << line << "\n";
}

void SyncWrite::createFile()
{
	m_wStream.open(m_wFilename, std::ios::out);
	m_wStream.close();
}

et le main pour tester :

void thread(void* data, int min, int max) {
	SyncWrite *writer = static_cast<SyncWrite*>(data);

	for (int i = min; i < max; ++i) {
		std::string i_str = std::to_string(i);
		writer->LockedWrite(i_str);
	}

}

int main(int argc, char* argv[]) {
	std::string name = "test.txt";
	SyncWrite writ(name);


	boost::thread t1(thread, &writ, 0, 10);
	boost::thread t2(thread, &writ, 10, 20);
	boost::thread t3(thread, &writ, 20, 30);


	writ.close();

	return 0;
}



je m'attendais, dans le fichier résultat a avoir quelque chose de la forme

1
11
2
21
22
23
3
12
13
14
4
5
...

mais le résultat semble être toujours

0
...
9
10
...
19
20
...
29

ou alors

10
...
19
1
...
10
20
...
30

ou bien

20
...
29
10
...
19
1
...
9

Donc les threads se font toujours l'un puis l'autre puis l'autre. jamais les 3 en même temps.
Est-ce que c'est un probleme dans mes threads, ou bien dans ma façon de lock mon fichier pour l'écriture? je dois avouer que j'ai pas l'habitude de bosser avec des threads; et que j'ai jamais essayé de multithreadé des I/O dans un fichier donc ça m'embête un peu.
Je précise que j'ai récupérer le code pour le write synchronisé sur google :/

Est-ce que quelqu'un sait ce qui se passe? :(


Je vous souhaite une très bonne fin de journée,

Cordialement,

R.S







  • Partager sur Facebook
  • Partager sur Twitter

« Je n’ai pas besoin de preuve. Les lois de la nature, contrairement aux lois de la grammaire, ne permettent aucune exception. »
D. Mendeleïev

18 juin 2019 à 17:39:13

Bonjour, pense à faire les 

t1.join();
t2.join();
t3.join();

Dis-moi si cela t'a aidé.

Il semble que ton code se passe de manière synchrone effectivement

Deux possibilités :

1) soit c'est parce que c'est extrêmement rapide donc pas de différence au niveau du résultat 

2) je ne connais pas encore shared_mutex, mais je me demande si ce n'est pas dû à cela. J'ai l'impression que ton mutex priorise peut-être tes tâches.

J'espère avoir pu aider.

Cordialement

-
Edité par pseudo-simple 18 juin 2019 à 17:39:41

  • Partager sur Facebook
  • Partager sur Twitter
18 juin 2019 à 17:57:20

AMA, le temps que chaque thread démarre, le précédent a déjà terminé. Rajoute des sleeps ou plus de nombres pour voir plus de chaos.

----------------------

Sinon.

Une façon très C++ consiste à spécialiser le filebuf interne au fstream par une classe dédiée (à écrire qui sera fille de streambuf) qui bufferise chaque entrée reçue dans des stringbuf qui sont thread-locals. Sur le message de dump (dans un thread) (j'ai oublié son vrai nom), on peut alors locker le filebuf de destination et dumper le stringbuf threadlocal (et le vider au passage). Pas oublier de tout vider sur le close.

Ainsi, l'écriture devient naturellement synchronisée entre deux flushs sans que l'on ait à explicitement manipuler les threads. Après, on ne peut avoir strictement aucune contrôle sur quelle ligne s'écrit avant quelle autre. Pour des vrais fichiers, autant dire que l'écriture MT est un vrai problème complexe qui nous interdit beaucoup de formats.
(autre message à peine plus détaillé https://www.developpez.net/forums/d1876828/c-cpp/cpp/threads-processus/cout-plusieurs-threads/#post10379276 ; il me semble avoir bien décrit comment faire dans le passé, mais je ne retrouve pas)

Pour des logs... on ne s'embête pas et la solution que j'ai décrite se retrouve sous une forme ou un autre dans les diverses bibliothèques de log justement.

TL;DR:
- si tu veux écrire un vrai fichier, tu ne vas pas y arriver car tu vas vouloir contrôler l'ordre d'écriture des lignes -> utilise des formats dédiés (genre netcdf non compressé si je me souviens bien)
- si c'est pour résoudre un problème plus léger et ne pas perdre de temps, utilise un framework de log tel que spdlog -- d'autant plus si c'est pour logguer
- si tu as un niveau plus avancé et que tu t'ennuies, tu peux te renseigner sur la mécanique interne des flux C++ et souffrir de une à bien plus de semaines -- j'ai donné les mots clés principaux et les divers intervenants à plugguer ensembles
- tu peux bien évidemment toujours mettre des mutex autour de tes écritures de lignes, mais tu vas alors perdre tout l'intérêt de multithreader tes traitements.

-
Edité par lmghs 18 juin 2019 à 18:04:50

  • Partager sur Facebook
  • Partager sur Twitter
C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
20 juin 2019 à 22:10:01

Généralement le processeur est bien des fois plus rapide que l'I/O, et paralléliser l'écriture sur un disque dur ne sert tout simplement à rien. 

  • Partager sur Facebook
  • Partager sur Twitter

Mes articles | Nazara Engine | Discord NaN | Ma chaîne Twitch (programmation)

20 juin 2019 à 22:23:13

Lynix a écrit:

Généralement le processeur est bien des fois plus rapide que l'I/O, et paralléliser l'écriture sur un disque dur ne sert tout simplement à rien. 


En effet, et je dirais que tu pourrais même avoir de mauvaises surprises si plusieurs threads viennent ajouter des données sur un fichier. 

S'il n'y en a qu'un seul qui fournit les données, ou pourra envisager quelques optimisations du compilateur, ou de l'OS, de la cache, voir du matos. 

Si tu es en multithreading, va prévoir dans ordre vont arriver les données... 

Si tu fais du multithreading, c'est pour faire du calcul, et il vaut mieux qu'il y ait un seul thread qui s'occupe d'écrire sur le disque que plusieurs.

  • Partager sur Facebook
  • Partager sur Twitter

Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

24 juin 2019 à 8:47:41

Yooo!
Désolé, j'étais pas dispo pendant quelques jours pour cause de déplacement pro.

Lynix a écrit:

Généralement le processeur est bien des fois plus rapide que l'I/O, et paralléliser l'écriture sur un disque dur ne sert tout simplement à rien. 


Est-ce que c'est le cas pour plusieurs fichiers aussi?
Dans mon cas , j'ai une liste de points, et plusieurs fichiers ouverts, et je dois écrire les points dans les bons fichiers (en fonction de leur X,Y et Z) donc pour détailler un peu l'algo :

Liste de points pts;
8 fichiers ouverts.

Pour chaque point de pts
 lire le point, 
 regarder son X,Y et son Z
 écrire le point dans le bon fichier en fonction de ces valeurs.

ça ne serai pas plus efficace dans ce cas-là?

Est-ce que si je fais quelque chose comme :

Liste de points pts;
N fichiers ouverts
 
Créer N nouvelles listes (correspondante aux fichiers ouverts)

(multithreader)
pour tous les points dans pts :
  mettre les points dans la bonne liste

(multithreader?)
Pour toutes les nouvelles listes
  écrire les listes dans les fichiers



YES, MAN :

c'était effectivement un probleme de vitesse d'execution. Avec des nombres plus grands, le résultat obtenu est plus celui que j'attendais.

Fvirtman :
Merci pour ces conseils, je vais essayé de réfléchir à ce que tu m'as dis (j'avoue que très rapidement comme ça, c'est un exercice assez nouveau pour moi et je comprends pas tout, mais je vais me renseigner).
Pour ce qui est de mes fichiers, c'est juste des fichiers de points. une ligne correspond à un point pour le moment, qu'importe leur ordre. Je ne sais pas dans quelle parti de fichiers ça rentre ^^


Merci pour vos réponses en tout cas!


  • Partager sur Facebook
  • Partager sur Twitter

« Je n’ai pas besoin de preuve. Les lois de la nature, contrairement aux lois de la grammaire, ne permettent aucune exception. »
D. Mendeleïev

24 juin 2019 à 9:36:59

KirbXCoucou a écrit:

Lynix a écrit:

Généralement le processeur est bien des fois plus rapide que l'I/O, et paralléliser l'écriture sur un disque dur ne sert tout simplement à rien. 


Est-ce que c'est le cas pour plusieurs fichiers aussi?
Dans mon cas , j'ai une liste de points, et plusieurs fichiers ouverts, et je dois écrire les points dans les bons fichiers (en fonction de leur X,Y et Z) donc pour détailler un peu l'algo :

Liste de points pts;
8 fichiers ouverts.

Pour chaque point de pts
 lire le point, 
 regarder son X,Y et son Z
 écrire le point dans le bon fichier en fonction de ces valeurs.

ça ne serai pas plus efficace dans ce cas-là?

Est-ce que si je fais quelque chose comme :

Liste de points pts;
N fichiers ouverts
 
Créer N nouvelles listes (correspondante aux fichiers ouverts)

(multithreader)
pour tous les points dans pts :
  mettre les points dans la bonne liste

(multithreader?)
Pour toutes les nouvelles listes
  écrire les listes dans les fichiers


Oui, écrire dans plusieurs fichiers en même temps est plus rapide que d'écrire dans les fichiers de façon séquentielle. Mais tu n'as pas besoin de threads pour ça. Il te suffit d'écrire dans les fichiers de façon asynchrone.

Le principe est que (depuis un même thread) tu donnes l'ordre d'écrire les données dans plusieurs fichiers à la fois sans attendre que l'écriture précédente soit terminée (et attendre que l'ensemble soit terminé).

Une bibliothèque multiplateforme assez connue qui fait ça bien est libuv, si tu as la foi tu as aussi les API systèmes (posix ou api win32) qui font ça très bien (avec le gros désavantage de ne pas être portable et d'être assez obscurs).

  • Partager sur Facebook
  • Partager sur Twitter

Mes articles | Nazara Engine | Discord NaN | Ma chaîne Twitch (programmation)

24 juin 2019 à 10:03:58

Ah! Merci pour la lib! Je vais aller regarder ça de ce pas!

  • Partager sur Facebook
  • Partager sur Twitter

« Je n’ai pas besoin de preuve. Les lois de la nature, contrairement aux lois de la grammaire, ne permettent aucune exception. »
D. Mendeleïev