Problème de Thread

[Résolu]
Signaler
Messages postés
5487
Date d'inscription
dimanche 4 août 2002
Statut
Modérateur
Dernière intervention
20 juin 2013
-
Messages postés
5487
Date d'inscription
dimanche 4 août 2002
Statut
Modérateur
Dernière intervention
20 juin 2013
-
Bonjour,
Bon je suis pas encore tout à fait au point avec les Threads. La question est certainement pas très compliquée.
J'ai une forme qui contient un bouton "search". Quand on clique dessus, le programme commence à faire une recherche qui peut durer un certain temps (tout dépend).
Avant, tout mon code (Recherche + Affichage de la recherche) se faisait dans l'event click de mon bouton "search". Le problème était évidemment que si la recherche prennait du temps, rien ne se passait pendant plusieurs secondes (et la fenetre de recherche était "bloquée", on ne peut plus la bouger).


J'ai donc créee une méthode Search, que je démarre dans l'event click de mon bouton avec un thread, et avant ça je met un petit message dans un statusBar pour avertir l'utilsateur que la recherche a commencée, ça donne qqch comme :


// Search recherche et affiche le résultat
myStatusBar.Text = "Recherche en cours... patientez...";
Thread threadSearch = new Thread(new ThreadStart(Search));
threadSearch.Start();


Le problème c'est que le code suivant me retourne une erreur, il me dit qu'un contrôl créee par un thread ne peut pas être parenté à un control d'un thread different. C'est normal, puisque dans la méthode search, je recherche et ensuite j'affiche ma recherche (dans un datagrid). Donc je me dis que dans ma méthode Search, je dois juste faire la recherche, et le reste dans l'event de mon bouton "search" :


// Cette fois, search ne fait que la recherche !
myStatusBar.Text = "Recherche en cours... patientez...";
Thread threadSearch = new Thread(new ThreadStart(Search));
threadSearch.Start();
AfficheLesResultats();


J'arrive à un autre problème : AfficheLesResultats se sert évidemment d'une variable globale (une collection) qui contient les résultats. Mais cette variable n'est bien sûre pas initialisée tant que la méthode Search() n'a pas été executée. Donc j'ai un null pointer exception. J'ai essayé un threadSearch.Join() juste avant la méthode AfficheLesResultats(), la ça ne plante plus, mais je n'ai rien de plus qu'au début (la form est figée, on peut plus la bouger et c'est évidemment pas le but).


J'ai déjà rencontré ce problème pleins de fois, et j'ai jamais réussi à régler ça proprement, quelqu'un peut-il m'aider? Merci !

[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever

10 réponses

Messages postés
3466
Date d'inscription
lundi 16 octobre 2000
Statut
Modérateur
Dernière intervention
30 octobre 2008
54
Voila comment je procède.

Contexte : Un splashScreen qui active / desactive des fonctionnalités de l'appli selon certaines conditions



Je lance mon thread :

t = new Thread(new ThreadStart(Start));

t.IsBackground = true;

t.SetApartmentState(ApartmentState.STA);

t.Start();



Dans la méthode Start, j'ai ça (Condition : connexion a la bdd a reussie ):

if (cp.Connect())

{

BeginInvoke(new EnableControl(mainform.SwitchInterface), new object[] { true });

...

}

else

{

BeginInvoke(new EnableControl(mainform.SwitchInterface), new object[] { false });

...

}



le délégué EnableControl :

private delegate void EnableControl(bool enable);



et la méthode SwitchInterface dans ma classe MainForm :

public void SwitchInterface(bool enable)

{

...

}



Donc j'imagine que tu pourrais faire pareil, en passant comme parametre
qqchose comme un DataSet contenant le resultat de ta recherche.

Ou alors tu as reussi d'une autre façon ?



Mx
Messages postés
5487
Date d'inscription
dimanche 4 août 2002
Statut
Modérateur
Dernière intervention
20 juin 2013
56
Merci pour l'aide et l'explication !
Bon j'ai enfin réussi à faire plus ou moins marcher mon truc. Voici ma solution (basée sur la tienne):


private delegate void SearchDelegate(MoviesCollection mCol);


// Clique effectué sur le bouton de recherche
private void btnSearch_Click(object sender, System.EventArgs e)
{
this.statusBarSearch.Text = "Recherche en cours... patientez...";
// Démarre la recherche dans un nouveau thread
Thread t = new Thread(new ThreadStart(Searching));
t.IsBackground = true;
t.Start();
}


// Commence la recherche
private void Searching()
{
// La collection qu'il faut remplir et qui va être affichée dans le datagrid
MoviesCollection moviesCol = null;
// J'ai ici un object Search que j'ai conçu qui me permet d'effectuer mes
// recherches selon certains critères
Search s = new Search(_variable);
/* Ensuite un code assez long pour rechercher, voici un aperçu simplifié */
moviesCol = s.Go();
// On passe la collection (qui est la réponse de la recherche) au delegate
// que j'ai crée, qui prend justement une telle collection en argument :-)
object[] mCol = { moviesCol };
myDataGrid.BeginInvoke(new SearchDelegate(Invocation), mCol);
}


// Appel asynchrone, on reçoit en argument l'object passé lors de l'appel de BeginInvoke
private void Invocation(MoviesCollection moviesCol)
{
// Ici je fais qqe traitement sur ma collection
// Puis je remplis mon datagrid avec ma collection
}


Ceci dit il reste encore un miniscule problème : dans la méthode Invocation, selon le résultat de la recheche, je change la taille de ma form ou se trouve le datagrid (je l'agrandis si y'a beaucoup de résultat, pour que ce soit plus conviviale). Ca semble marcher.
Maintenant, puisque tout est fait comme je le souhaite, je peux bouger ma forme comme j'en ai envie pendant la recheche, ça bloque plus comme avant (c'était le but :-) ). Si je relache la forme AVANT que le code passe par la méthode Invocation, pas de problème, par contre si je bouge ma forme à ce moment, alors il semble que le code ne soit pas exécuter (la forme ne s'agrandit pas).


En tout cas, merci du coup de main.

[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever
Messages postés
2368
Date d'inscription
mardi 17 avril 2001
Statut
Modérateur
Dernière intervention
26 décembre 2007
20
As tu regardé du côté de l'asynchrone , c'est ce qu'il y a de plus simple pour gérer ce genre de cas.

::|The S@ib|::
MVP C#.NET
Messages postés
3246
Date d'inscription
lundi 25 avril 2005
Statut
Modérateur
Dernière intervention
27 octobre 2012
39
Pour eviter que ta forme "freeze" tu peux faire plus simple:



private void searchButton_Click( object sender, System.EventArgs args )
{
searchButton.Enabled = false; // Pour ne pas entrer a nouveau dans la fonction


while ( !found )
{
// Ici tu recherches etape par etape pas tout d'un coup


Application.DoEvents( ); // Traite les evenements
}



searchButton.Enabled = true;
}


Sinon pour faire patienter un thread utilise l'expression "lock"
Messages postés
3466
Date d'inscription
lundi 16 octobre 2000
Statut
Modérateur
Dernière intervention
30 octobre 2008
54
J'ai aussi souvent eu ce prob de modifications inter-thread interdites.

Pour le moment, j'ai pu resoudre ces probs via BeginInvoke().



Fais nous part de comment tu as pu resoudre ton souci, ca peut toujours servir

Mx
Messages postés
5487
Date d'inscription
dimanche 4 août 2002
Statut
Modérateur
Dernière intervention
20 juin 2013
56
Dès que j'ai résolu le problème, je te fais part de ma solution !

[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever
Messages postés
3246
Date d'inscription
lundi 25 avril 2005
Statut
Modérateur
Dernière intervention
27 octobre 2012
39
Je persiste a dire que tu pouvais te passer d'un second thread en utilisant la methode "DoEvents" a intervalles reguliers pendant la recherche pour ne pas freezer ta forme . Sinon as tu essaye d'initialiser ta collection avec des valeurs bidons ( a zero ) avant de lancer ton second thread ( sans la methode join ) puis de reactualiser ta collection avec les vrais valeurs une fois que ton thread est stoppe ( ThreadState.Stopped ).

Si tu as un probleme de synchronisation alors utilise le mot cle "lock" pour empecher un second thread de modifier une partie de code avant que le premier thread est fini son boulot, mais la tu vas encore avoir un probleme de blocage de ta forme.


Tu peux aussi utiliser la fonction "join" avec un parametre de temps "join( 20 )" et la tu affiches tes resultats petis a petits, pas tout d'un coup ! comme la commande Rechercher de windows dans le menu demarrer !


Le mot cle "volatile" peut aussi etre utile dans une application multithread.
Messages postés
5487
Date d'inscription
dimanche 4 août 2002
Statut
Modérateur
Dernière intervention
20 juin 2013
56
Je n'en doute pas, mais l'autre solution me paraît beaucoup plus propre tout de même.
Pour faire cet appel asynchrone, on fait comment exactement?

Quand on clique sur Ok :
{
...
myDataGrid.BeginInvoke(myDelegates);
...
}

// Appelé par le delegates
public void Test()
{
// on cherche et on affiche ?
}

[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever
Messages postés
5487
Date d'inscription
dimanche 4 août 2002
Statut
Modérateur
Dernière intervention
20 juin 2013
56
A vrai dire je n'ai pas encore réussi
Petite question sur ton code : A quel object appartient ta méthode BeginInvoke ?
(et accessoirement, t.IsBackground et t.SetApartmentState(ApartmentState.STA) servent à quoi exactement ?).

[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever
Messages postés
3466
Date d'inscription
lundi 16 octobre 2000
Statut
Modérateur
Dernière intervention
30 octobre 2008
54
Le BeginInvoke est appelé depuis la form qui sert de SplashScreen, donc j'imagine que c'est une méthode de la classe Control



Pour ce qui est du IsBackground, c'est pour spécifier que c'est un
thread d'arriere plan, donc qui n'empeche pas un processus de s'arreter.

Et le SetApartmentState (qui est une méthode 2.0 en fait, mais dans la
version 1.1, on peut acceder a la propriété ApartmentState), bah je te
renvoies a l'aide du framework parce que comme ca c'est dur a resumer
(et a vrai dire, ca reste un peu flou pour moi)

Mx