Bienvenue sur le forum !

Si vous souhaitez rejoindre la communauté, cliquez sur l'un de ces boutons !

Qt : 5.10.0 - Qt Creator : 4.5.0 - Qt Installer : 3.0.2 - JOM : 1.1.2 - Qt Build suite : 1.7.0 - VS Qt 5 : 2.0.0

Problème de thread

16 Feb modifié dans Qt / Langage C++
Bonjour à tous,
J’essaye de créer une application qui me permet de sauvegarder un fichier ou un répertoire d’origine vers un répertoire de destination. voir image 1image
Le problème c’est que quand le processus de copie commence, la fenêtre de l’application devient bloquante, c’est-à-dire que je ne peux même pas la réduire ou la fermée avant que la copie ne soit terminée.
Pour cela j’ai pensé à utiliser les threads, alors j’ai procédé comme suit :
J’ai créé la fonction suivante :
Remarques : j’ai remplacé une partie du code par des points, afin de réduire la taille du message)
int copyDirFile(string sourceFolderString, string destFolderString, ARG_INFO_STRUCT& Info_structureRecu)// string* source, string* dest)
{
if(TEMOIN_CREER_THREAD)
{
QMessageBox::information(0, "TEMOIN_CREER_THREAD", "TEMOIN_CREER_THREAD");
Info_struct_local = Info_structureRecu;
}

qDebug() << "je suis bien dans la fonction copyDirFile";
replace(sourceFolderString.begin(), sourceFolderString.end(), '/','\\');
replace(destFolderString.begin(), destFolderString.end(), '/','\\');

const char* sourceFolder = sourceFolderString.c_str();
const char* destFolder = destFolderString.c_str();

//******************************************
//Si c'est un fichier
FILE* sourceFile = fopen(sourceFolder, "rb");
if(sourceFile != NULL)
{
//On recupère le nom de fichier, on convertit de char a string pour pouvoir faire des manipulations
string baseNameFile = sourceFolder;
baseNameFile = baseNameFile.substr(baseNameFile.find_last_of("\\")+1, baseNameFile.size());
//On crée le fichier final de déstination
……………………..
………………………………………

fclose(sourceFile);

ARG_COPY_FILE Copy_file_struct = {
Copy_file_struct.source = sourceFolder,
Copy_file_struct.destination = destFolderComplet,
};


if(pthread_create(&thread_copy, NULL, &copyFile, (void*)&Copy_file_struct))
return 5;

if(pthread_join(thread_copy, NULL))
return 8;

//majInfos2();

}
//Si ce n'est pas un fichier
else
{
//On doit tester pour savoir si c'est un répertoire, on essayant de l'ouvrir
DIR *dir = opendir(sourceFolder);
struct dirent *ent;
//Si l'ouverture s'est bien déroulée, donc c'est un répertoire
if(dir != NULL)
{
//On récupère la base du nom du répertoire d'origine pour l'utiliser à créer le répertoire de destination.
……………………………………….
//On met le curseur en position 2 pour éviter le (.) et les (..) des répertoires actuel et précédent.
seekdir(dir,2) ;
//On liste tous les élément contenus dans le répertoire
while ((ent = readdir (dir)) != NULL)
{
//On forme le fichier ou le repertoire de déstination, tout dépend de l'élément qu'on va lire
…………………………………………….
//On ouvre le fichier
FILE* fichier = fopen(actualFolderSource, "rb");
ARG_COPY_FILE Copy_file_struct = {
Copy_file_struct.source = actualFolderSource,
Copy_file_struct.destination = destFolderComplet,
//Copy_file_struct.mutex = PTHREAD_MUTEX_INITIALIZER,
};
//Si c'est un fichier
if(fichier != NULL)
{
//*source = sourceFolder; *dest = destFolderComplet;
Maj_struct_local.source = actualFolderSource;
Maj_struct_local.destination = destFolderComplet;

//On crée les thread

if(pthread_create(&thread_copy, NULL, &copyFile, (void*)&Copy_file_struct))
return 5;
if(pthread_join(thread_copy, NULL))
return 8;

fclose(fichier);
}
//Si ce n'est pas un fichier donc c'est surement un réperoire, on appelle donc récursivement la même fonction
else
copyDirFile(actualFolderSource, fullNameDirDest, Info_struct_local);
}
}
//Ce n'esi pas un fichier et ce n'est pas un répertoire
else
return 3;
}
//Si on arrive jusqu'ici, cest que tout est bien passer
return 0;
}
Et voici la fonction copyFile :
void *copyFile(void *arguments)
{
qDebug() << "je suis bien dans la fonction copyFile";
qDebug() << "temoin = " << TEMOIN;
ARG_COPY_FILE *args = (ARG_COPY_FILE *)arguments;
//On définit les fichiers sources et déstination
FILE* fSrc;
FILE* fDest;
char buffer[1024];
int NbLus;
//On ouvre le fichier source
if ((fSrc = fopen(args->source, "rb")) == NULL)
{
//On retourne 1 pour dire que le fichier source n'a pas était ouvert.
pthread_exit(NULL);
}
//On ouvre le fichier destination
if ((fDest = fopen(args->destination, "wb")) == NULL)
{
//On ferme le fichier source
fclose(fSrc);
//On retourne 2 pour dire que le fichier de déstination n'a pas était ouvert
pthread_exit(NULL);
}

//Si tout se passe bien on lit les données du fichier source et on les enregistre dans le fichier de déstination
while ((NbLus = fread(buffer, 1, 1024, fSrc)) != 0)
fwrite(buffer, 1, NbLus, fDest);


//On ferme les deux fichier
fclose(fDest);
fclose(fSrc);

Info_struct_local.textEdit->append("copie de " + QString::fromStdString(Maj_struct_local.source) + " vers " + QString::fromStdString(Maj_struct_local.destination));
//Info_struct_local.textEdit->update();
Info_struct_local.progressBarre->setValue(Info_struct_local.progressBarre->value()+1);
//On retourne 0 pour dire que tout c'est bien passé
pthread_exit(NULL);
}
Le problème c’est que la fenêtre est toujours bloquante.

Et c’est pour cela que j’ai besoin de vos lumières
Merci d’avance pour votre aide.
jpg
jpg
image1.jpg
200K

Réponses

  • Bonsoir !

    Avis d'un fainéant : les threads c'est de l'artillerie lourde qui en l'occurrence ne se justifie pas.
    Ce que je ferais à ta place :
    1/ rendre inactifs ("disables") ton IHM, sauf les décorations des fenêtres
    2/ exécuter ton code dans des boucles for ou while à la fin desquelles tu glisses un
    QCoreApplication::processEvents ();
    tu garderas ainsi la possibilité de réduire, agrandir, ... la fenêtre pendant un traitement.
  • Hello

    Sinon, QtConcurrent... les threads en mode facile ! (mais ça ajoute une lib Qt qui n'est pas toute légère il me semble)
  • Merci à tous pour vos réponses, mais comme je l'ai précisé dans mon poste j'aimerai bien éviter les bibliothèques externe. mais je vais comme même essayer
    QCoreApplication::processEvents ();
    et je vous tiendrai au courant.
    et merci encore.
  • Bin quoi, QCoreApplication est une classe purement Qt ! Je ne comprends pas...
  • j'ai pas vraiment le choix, il faut que mon application fonctionne et après je lance une révolution pour mon indépendance en performant mes thread
  • 20 Feb modifié
    Bonjour deja quand je vois ta fonction de thread Copyfile,tu ne devrais pas acceder a des ressources de l'ihm dedans puisque c'est une fonction de thread secondaire.
    ensuite si tu utilises Qt il serait plus judicieux et plus facile d'utiliser des Qthreads que les threads pthreads de l'api linux.
  • 20 Feb modifié
    Salut Mourad2009,

    Il faut que tu mettes l'ensemble du processus de copie dans un thread, sinon tu bloqueras toujours dans la boucle while.
    Solution simple mais pas terrible: ajouter qApp->processEvents() dans la boucle while.
    Si je comprend bien tu crées un thread par fichier copié, c'est pas trés utile, l"écriture sur disque se fait en file d'attente, donc chaque thread attend son tour ...
    De plus tu crées un nombre indéterminé de threads, ça peut devenir catastrophique en terme de performance car le cpu passe son temps à passer d'un thread à l'autre.

    [edit] je connais pas bien les pthreads, mais si je lis la doc:
    The pthread_join() function shall suspend execution of the calling thread until the target thread terminates
    donc ça bloque le thread principal ?
    [/edit]

    Comme dit plus haut, ne jamais accéder à des éléments de l'interface dans un thread secondaire -> crash potentiel.
    Tu dois créer des méthodes dans ton thread qui te permettes de communiquer avec lui: pour connaitre l'état du thread (en cours, terminé, erreur, etc) et pour pouvoir l'interrompre.

    pthreads n'est pas spécifique à linux c'est du POSIX, donc portable (pour Windows il faut peut-être ajouter la lib adhoc) mais c'est du bas niveau pas simple à gérer.

    La gestion des threads avec Qt est assez particulière, j'ai lu des choses totalement contradictoires à ce sujet, doc incomplète, des exemples d'utilisation faux.
    Je m'y suis intéressé il y a quelques temps, j'ai même posé une question à ce sujet sur ce forum et franchement c'est pas clair.

    Voici un lien que j'espère fiable ( je n'ai pas lu/testé)
    Threads_Events_QObjects

    Bon courage,
  • Pour les QThreads, comme c'est expliqué ici http://doc.qt.io/qt-5/qthread.html, tu as deux manieres de les utiliser dans Qt:
    -soit tu n'as pas besoin d'envoyer des signaux à ton thread et là tu peux faire une classe derivant de QThread en redefinissant la fonction run dans laquelle tu mets ton traitement de thread secondaire
    -soit tu as besoin que ces threads repondent à des signaux et tu fais une classe derivant de QObject dans laquelle tu mets ton traitement dans un ou des slots et tu fais un worker->moveToThread(&workerThread); c'est à dire sur un thread secondaire QThread et tu le demarres avec workerThread.start();
    c'est pas plus compliqué que cela si tu regardes cette page de documentation sur doc.qt.io

  • Bonjour à tous,
    Je vous remercie pour les conseils et les suggestions que vous avez voulu m’en faire profiter.
    Après plusieurs modifications, je me suis résigné à utiliser
    QCoreApplication::processEvents();
    Car je crois que les thread ne sont pas adaptés à ma structure de code, vue que je vais modifier les valeurs d’un QProgressBar et le contenu d’un QTextEdit, et comme les thread ne permettent pas de communiquer avec les objets de l’interface graphique, donc j’ai choisi de rester dépéndant de Qt et d’utilisé
    QCoreApplication::processEvents(); dans la boucle while de la fonction copyFile.
    Et avec ça j’arrive à interagir avec mon interface graphique pendant le processus de copie.
    En tout cas merci encore pour vos réponses
  • Salut,
    Car je crois que les thread ne sont pas adaptés à ma structure de code, vue que je vais modifier les valeurs d’un QProgressBar et le contenu d’un QTextEdit,
    Un thread peut émettre des signaux, donc il peut tout à fait permettre la modification de l'ui.
  • oui comme dit babaOroms, tu peux emettre un signal depuis ton thread secondaire pour signaler par exemple que tu as fini ton traitement et renvoyer un paramètre ou envoyer un parametre à ton interface graphique
  • Merci pour vos conseils. je me pencherez sur cette idée plus tard, mais pour l'instant l'application doit fonctionner, je vous tiendrai au courant.
    Merci à tous.
Connectez-vous ou Inscrivez-vous pour répondre.