Form splitté en plusieurs Thread

nagaD.scar Messages postés 4272 Date d'inscription samedi 8 septembre 2007 Statut Membre Dernière intervention 4 janvier 2023 - 4 nov. 2013 à 13:54
nagaD.scar Messages postés 4272 Date d'inscription samedi 8 septembre 2007 Statut Membre Dernière intervention 4 janvier 2023 - 12 nov. 2013 à 07:50
salut à tous !

Voilà, je fais actuellement un traitement qui en gros concatène 2 fichier en supprimant les doublons (sans rentrer dans les détails). Pour que l'utilisateur puisse voir que quelque chose se passe j'ai mis un petit gif animé (ou Trunk et Goten fusionnent =D) ainsi qu'une listBox qui trace les évènements, masquable (masqué par défaut). Mon problème est que le nombre d'information envoyé à ma listeBox est trop rapide (c'est une lecture de fichier donc ca va vite ^^) et fait ramer ma forme lorsque la liste n'est pas masqué.

J'ai donc pensé que la meilleur solution consisterai à ce que la listBox soit gérer indépendamment du reste de la form dans un thread à part entière, mais c'est là que je coince : je ne vois pas comment faire ... générer une listBox via un thread ne fonctionne pas VU que la form à son propre thread, et donner la listBox en paramètre à un thread revient au même vu que ca revient à un invoke (je suis pas sûr d'être très clair).

Je cherche donc des pistes à explorer ! Tout avis pourra m'être utile ;). Car en cherchant sur internet, je trouve bien tout ce que je veux concernant les thread, mais pas comment gérer deux thread sur une même form (c'est toujours vis à vis du traitement, pour qu'il ne bloque pas la forme).

merci d'avance.
Naga

5 réponses

yann_lo_san Messages postés 1137 Date d'inscription lundi 17 novembre 2003 Statut Membre Dernière intervention 23 janvier 2016 26
4 nov. 2013 à 20:56
Salut,

il faut juste faire un delegate pour que ça marche


// Si 1 thread appelle directement cette fonction, ça ne marchera pas

public void ModifFormulaire(string sNewMsg)
{
maListBox.Items.Add(sNewMsg);
}


// Par contre, celui ci marchera :

public delegate void AddListItem(String sNewMsg);
public AddListItem myDelegate;

public void ModifFormulaire(string sNewMsg)
{
if( maListBox.InvokeRequired )
{
myDelegate = new AddListItem(ModifFormulaire);
this.Invoke(myDelegate , new Object[] {sNewMsg});
}
else
{
// sur le thread du formulaire
maListBox.Items.Add(sNewMsg);
}
}



bye...
0
nagaD.scar Messages postés 4272 Date d'inscription samedi 8 septembre 2007 Statut Membre Dernière intervention 4 janvier 2023 17
Modifié par nagashima le 5/11/2013 à 07:53
salut.

Oui je connais le concept, mon problème est que le flux d'information est trop important => ce qui fait que, vu que j'ajoute de nombreuses données à ma liste box de manière très rapide, l'interface graphique ramme (elle ne freeze pas, elle galère).

Ce que je cherche serai plutot de divisier ma forme graphique de manière à ce que ma liste soit gérée depuis un thread (par exemple, la Form est gérée dans un thread, si on ouvre une Form soeur c'est un nouveau thread => basiquement c'est ce que je veux faire).

mais merci essayer de m'aider ;)

naga


PS : pour ma part, je déclare de cette manière :

 public partial class f_main : Form
{
public delegate void parStr(string s);
private void snitch(string s)
{
if (InvokeRequired)
{
if (this.InvokeRequired)
{
this.Invoke(new parStr(snitch), s);
return;
}
}
if (!bShowInfo)
{
List_Snitch.Items.Insert(0, s);
}
}
...
}

public class cFusionFicVin
{
public Action<string> imDoin = delegate(string s) { };


private void snitch(string s)
{
using (StreamWriter sw = new StreamWriter(sFicSnitch, true, Encoding.UTF8))
sw.WriteLine(s);

imDoin(s);
}
}


histoire de ne pas avoir à faire d'invoke dans les appels.
0
yann_lo_san Messages postés 1137 Date d'inscription lundi 17 novembre 2003 Statut Membre Dernière intervention 23 janvier 2016 26
5 nov. 2013 à 13:00
Re,

as tu essayé tout simplement de mettre un Application.DoEvents() après
chaque modif de la liste ?
0
nagaD.scar Messages postés 4272 Date d'inscription samedi 8 septembre 2007 Statut Membre Dernière intervention 4 janvier 2023 17
5 nov. 2013 à 13:39
re,
Le problème ne vient pas de là, le doEvent me permettrai dans le cas d'un jeux d'instruction de redonner la mains, mais dans mon cas c'est déjà le cas.

En fait, le problème est que j'appel mon action avec un interval de temps inférieur au temps qu'il faut pour la fonction "snitch" pour ajouter un élément à la liste ...



fait ce code, avec une Form qui contient une liste et une image (en prenant soins d'ajouter un gif aux ressources) :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace aps
{
public partial class Form1 : Form
{
public delegate void parStr(string s);
public Action<string> imDoin = delegate(string s) { };
public Form1()
{
InitializeComponent();
imDoin += addList;
Thread Test = new Thread(() => f());
Test.Start();
Test = new Thread(() => f());
Test.Start();
pictureBox1.Image = aps.Properties.Resources.giphy__1_;
}
private void f()
{
int i = 0;
while (true)
{
imDoin("Ligne Liste " + i.ToString());
i++;
Thread.Sleep(10);
}
}
private void addList(string s)
{
if (InvokeRequired)
{
if (this.InvokeRequired)
{
this.Invoke(new parStr(addList), s);
return;
}
}
listBox1.Items.Insert(0, s);
}

private void pictureBox1_Click(object sender, EventArgs e)
{

}
}
}


les ajouts dans la liste se font bien. Maintenant, commentes
Thread.Sleep(10);
et tu remarque que la forme est freezée => on ajoute à la liste tellement souvent que ca prend totalement le focus.

Donc l'idée serai que l'élément de list dans ma forme soit un thread graphique à part entière, comme si on ouvrait une fenêtre soeur
0
yann_lo_san Messages postés 1137 Date d'inscription lundi 17 novembre 2003 Statut Membre Dernière intervention 23 janvier 2016 26
8 nov. 2013 à 14:08
Salut,

avec 2 ou 3 modif, ça marche bien pour moi
(une seule i,nstance du delegate)
(un thread managé et pas local)
une version simplifié de tes méthodes anonymes
at aucun thread.Sleep() ou DoEvent

Voici mon formulaire

    public partial class Form1 : Form
{
public delegate void parStr(string s);

private parStr _delParStr;
private Thread _thread; // on doit pouvoir manager le thread (jamais local)


/// <summary>
/// .Ctor
/// </summary>
public Form1()
{
InitializeComponent();

// Une seule instance du delegate suffit
_delParStr = new parStr(addList);
}


/// <summary>
/// Form1_Load
/// </summary>
private void Form1_Load(object sender, EventArgs e)
{
label1.Text = "Form loaded...";
}


/// <summary>
/// start thread
/// </summary>
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Start thread...";

if (_thread == null)
{
(_thread = new Thread(new ThreadStart(threadFunc))).Start();

label1.Text = "threadState : " + _thread.ThreadState.ToString();
}
}


/// <summary>
/// threadFunc
/// </summary>
private void threadFunc()
{
try
{
int i = 0;
while (true)
{
addList("Ligne Liste " + (i++).ToString());
// ici le gif continue de bien tourner !
// et l'utilisateur à accès à l'UI
}
}
catch (ThreadAbortException)
{
_thread.Join(5000);
_thread = null;
Thread.ResetAbort();
}

addList("End threadFunc ...");
}


/// <summary>
/// Cancel thread
/// </summary>
private void button2_Click(object sender, EventArgs e)
{
if (_thread != null )
{
Cursor.Current = Cursors.WaitCursor;
label1.Text = "Cancelling thread...";
try
{
_thread.Abort();
label1.Text = "thread aborded...";
}
finally
{
_thread.Join(5000);
_thread = null;
label1.Text += " - thread null...";
Cursor.Current = Cursors.Default;
}
}
}


/// <summary>
/// addList
/// </summary>
private void addList(string s)
{
if (listBox1.InvokeRequired)
{
this.Invoke(_delParStr, s);
}
else
{
listBox1.Items.Insert(0, s);

// Si le thread fini par lui même, sans "Abord"
if (s.StartsWith("End threadFunc") && _thread != null)
{
_thread.Join(5000);
_thread = null;
label1.Text += " - ok thread null...";
}
}
}


/// <summary>
/// Form1_FormClosing
/// </summary>
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
button2.PerformClick();
}
}



bye...
0
yann_lo_san Messages postés 1137 Date d'inscription lundi 17 novembre 2003 Statut Membre Dernière intervention 23 janvier 2016 26
8 nov. 2013 à 14:17
...
et pour info,
j'ai importé le gif directement dans un pictureBox au niveau du design (sans passer par l'instruction sur l'appel aux ressources)
bye...
0
nagaD.scar Messages postés 4272 Date d'inscription samedi 8 septembre 2007 Statut Membre Dernière intervention 4 janvier 2023 17
8 nov. 2013 à 14:40
yopyop !
Oui mais le truc c'est que dans ton code, via le join, tu fais en sorte que justement les appels ne se chevauchent pas, mais pour ma part ce n'est pas possible dans le sens ou c'est un traitement qui envoi les infos (donc perte de vitesse).

Le code que j'avais fournis n'est pas celui que j'utilise, moi je n'ai qu'un seul thread ... enfin la solution sera plutot d'avoir deux Thread graphiques de manière à ce que l'un n'empiète pas sur l'autre.

Bon je t'avous ne pas avoir trop le temps de me pencher sur la question aujourd'hui, j'ai pas mal de taff, mais dès que j'ai un peu de temps libre j'essayerai de mettre cela en place et si je trouve je donnerai le code ;)
0
yann_lo_san Messages postés 1137 Date d'inscription lundi 17 novembre 2003 Statut Membre Dernière intervention 23 janvier 2016 26
8 nov. 2013 à 21:19
hello,

Sinon,
si la fonction de traitement asynchrone doit venir de l'extérieur,
tu peux toujours créer un "event" dans la classe externe et le faire abonner par le formulaire

// Utilise le pattern event de Microsoft

public class AddListEventArgs : EventArgs
{
public readonly string _sInfo;
public AddListEventArgs(string sInfo)
{
_sInfo = sInfo;
}
};


class ExternProcess
{
public delegate void AddListEventHandler(object sender, AddListEventArgs args);

public event AddListEventHandler AddList;

protected virtual void OnAddList(AddListEventArgs e)
{
if(AddList != null)
{
AddList(this, e);
}
}

// Le traitement externe en lui-même
private void doWork()
{
OnAddList( new AddListEventArgs("une info") );
}
};


///////// le formulaire s'abonne d'une manière ou d'une autre
ExternProcess _extProc;
// (...)
_extProc.AddList += new AddListEventHandler( addList );


// Enfin, AddList devient
private void addList(object sender, AddListEventArgs e)
{
// toujours if( listbox1.InvokeRequired ) ect.... )
// et la data à ajouter est dans e._sInfo
}


Voilà, le process externe peut faire transiter autant de données qu'il veut vers la liste du formulaire sans géner l'utilisateur

bye...
0
nagaD.scar Messages postés 4272 Date d'inscription samedi 8 septembre 2007 Statut Membre Dernière intervention 4 janvier 2023 17
9 nov. 2013 à 08:29
Salut !
non c'est toujours pas ca x) ce que tu me propose concerne toujours le traitement de la donnée (ou la transition). Ce que je cherche est vraiment coté gaphique
....
Par contre je verrai pour faire une seconde form soeur (non bloquant donc) sans bordure que je positionnerai à l'emplacement de la liste et je l'abonner. C'est gentil d'essayer de m'aider et de prendre le temps, je pense que je dois mal exprimer mon objectif
0

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

Posez votre question
yann_lo_san Messages postés 1137 Date d'inscription lundi 17 novembre 2003 Statut Membre Dernière intervention 23 janvier 2016 26
9 nov. 2013 à 16:39
Salut;

Oui, j'ai bien compris ton problème,
mais EN AUCUN CAS tu ne dois avoir 2 [STAThread] (graphique) dans ton application.

Cela est contre toute logique.

Bye...
0
nagaD.scar Messages postés 4272 Date d'inscription samedi 8 septembre 2007 Statut Membre Dernière intervention 4 janvier 2023 17
12 nov. 2013 à 07:50
salut, je ne vois pas en quoi c'est contre toute logiques dans le sens où c'est ce qui se passe avec ... bah tout en fait, vu que chaque application, bureau, contrôleur (tel que l'activX), etc. l'est ... mais je comprend mieux pourquoi tu n'orientais pas ton code dans ce sens donc ;).

Quand je pourrai je prendrai le temps de le faire et je te redirai ca ;) .

Bne journée.

naga
0
Rejoignez-nous