Problème de Thread

Résolu
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 - 6 mai 2005 à 10:30
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 - 10 mai 2005 à 08:09
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

MorpionMx Messages postés 3466 Date d'inscription lundi 16 octobre 2000 Statut Membre Dernière intervention 30 octobre 2008 57
9 mai 2005 à 09:37
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
3
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
10 mai 2005 à 08:09
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
3
TheSaib Messages postés 2367 Date d'inscription mardi 17 avril 2001 Statut Membre Dernière intervention 26 décembre 2007 23
6 mai 2005 à 11:13
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
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
6 mai 2005 à 13:44
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"
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
MorpionMx Messages postés 3466 Date d'inscription lundi 16 octobre 2000 Statut Membre Dernière intervention 30 octobre 2008 57
7 mai 2005 à 09:21
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
0
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
7 mai 2005 à 11:14
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
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
7 mai 2005 à 16:54
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.
0
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
7 mai 2005 à 19:28
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
0
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
9 mai 2005 à 12:18
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
0
MorpionMx Messages postés 3466 Date d'inscription lundi 16 octobre 2000 Statut Membre Dernière intervention 30 octobre 2008 57
9 mai 2005 à 12:37
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
0
Rejoignez-nous