2010-08-03 3 views
2

Pour être honnête, je suis un peu boiteux quand il s'agit de filetage;) Donc, je demande un peu d'aide.Synchroniser le sous-fil avec le fil principal - problème pas si évident

Supposons que nous ayons une sorte de contrôle sur lequel nous pouvons dessiner des graphiques. Il existe également une méthode qui dessine un graphique sur ce contrôle. Le problème est que la méthode de cartographie a accès uniquement à l'un des champs du contrôle et nous avons besoin d'actualiser le contrôle lorsque le graphique est prêt.

Alors supposons que notre contrôle ressemble à ça:

class ChartingControl : System.Windows.Forms.Control 
{ 
    public Canvas canvas; 
    public void Refresh(); 
    /* 
    ... other fields/methods 
    */ 
} 

Canvas est une classe utilisée pour dessiner l'image (quelque chose comme Graphics).
La méthode de cartographie a accès uniquement à l'objet canvas (nous ne pouvons pas changer), il ressemble à ça:

public static void DrawChart(canvas) 
{ /* draw */ } 

Cette méthode peut être appelée à partir d'un thread séparé, travailleur de fond, etc ... Et je besoin de synchroniser avec le thread principal et appelez Refresh() lorsque le graphique est prêt.

En ce moment, lorsque le graphique est prêt, je mis un drapeau sur l'objet de toile

public static void DrawChart(canvas) 
{ /* draw */ 
    canvas.Tag = true; // chart is ready 
} 

et j'ai un travailleur de fond courir à l'intérieur du contrôle de cartographie et d'écoute si le champ canvas.Tag a changé et si oui, il appelle Refresh()

Mais il semble que ma méthode est un peu rude, facile à échouer etc ... Yat-il une meilleure méthode pour l'améliorer?

Limitations:
- nous ne pouvons pas modifier la classe Canvas. La seule chose que nous pouvons utiliser est le champ Tag (de type object
- nous pouvons modifier la méthode de classe et le dessin ChartingControl
-. Il peut y avoir de nombreux contrôles de représentation graphique
-. Nous ne contrôlons pas la façon dont DrawChart est appelé Il peut être appelé dans un thread séparé, ou non. il est appelé ailleurs. Tout ce que nous pouvons faire est de créer le contrôle et la méthode DrawChart et essayer de les comunicate en quelque sorte

Solution
OK, je l'ai résolu de cette façon : dans ChartingControl J'ai créé un ManualResetEvent manualReset et un travailleur de fond.

travailleur Bacground attend le manualReset:

void backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) 
    { 
      manualReset.WaitOne(); // Wait for a chart to be ready 
    } 

et à la fin appelle la méthode Reset()

void backgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) 
    { 
     Refresh(); 
    } 

Je passe l'objet manualReset à l'intérieur canvas.Tag et à l'intérieur méthode de dessin, lorsque le graphique est prêt j'appelle manualReset.Set(); pour passer le signal que le graphique est prêt.

+0

La méthode DrawChart démarre un nouveau thread ou vous l'appelez dans un autre thread? –

+0

Je l'appelle dans un autre fil – Gacek

Répondre

1

OK, je l'ai résolu de cette façon: dans ChartingControl j'ai créé un ManualResetEvent manualReset et un travailleur de fond.

travailleur Bacground attend le manualReset:

void backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) 
    { 
      manualReset.WaitOne(); // Wait for a chart to be ready 
    } 

et à la fin appelle la méthode Reset()

void backgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) 
    { 
     Refresh(); 
    } 

Je passe l'objet manualReset à l'intérieur canvas.Tag et à l'intérieur méthode de dessin, lorsque le graphique est prêt j'appelle manualReset.Set(); pour passer le signal que le graphique est prêt.

+0

Vous avez fait le bon choix IMO - pouvez-vous mettre à jour votre OP au lieu de poster une réponse à votre propre message - il est plus facile de voir le problème et la solution en un seul endroit – zebrabox

0

Supposant vous traitez avec la synchronisation de l'accès à l'objet de toile, je me contenterai de recommander que vous faites
myChartingControl.Invoke(new Action(myChartingControl.Refresh));
d'un autre fil qui tire le tableau

+0

Du fil qui dessine le tableau, je n'ai pas accès au contrôle de cartographie:/ – Gacek

+0

mais vous en passant en quelque sorte une référence à la toile, non? Cela signifie que vous pouvez passer une référence au contrôle et prendre canvas à partir de là – ULysses

+0

Je crée une bibliothèque qui est utilisée ailleurs. Le contrôle contient un objet Canvas et dans l'application où il est utilisé, un nouveau contrôle est créé et ensuite 'DrawChart (chartingControl.canvas)' est appelé (directement ou par sous-thread) – Gacek

0

BackgroundWorker vous aidera

void StartDrawChart(){ 

     BackgroundWorker bw = new BackgroundWorker(); 
     bw.DoWork += new DoWorkEventHandler(bw_DoWork); 
     bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); 
     bw.RunWorkerAsync() 
    } 


    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     this.Refresh(); 
    } 

    void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     DrawChart(this.canvas); 
    } 
+0

Oui, ce serait la solution si j'avais le contrôle sur la façon dont la méthode' DrawChart' est appelée. Mais il est utilisé ailleurs et je n'ai aucun contrôle sur lui. Je vais mettre à jour ma question. – Gacek

Questions connexes