2012-03-29 6 views
3

Ich war ein einfaches WinForm der Zusammenstellung, die eine Anzahl von Threads zu Schleife 0-10000 laichen würde - der Zweck dieser - von Windows zu verlangsamen, um einige anderen Programmen langsam laufen zu lassen.C# Aktualisierung TextBox von mehreren Threads

Grundsätzlich hat die Form ein Textfeld, das ich zu dem Schleifenindex schreiben will von jedem Thread. Alles war in Ordnung für einen einzelnen Thread, aber da ich mehr Threads eingeführt habe, würde ich scheinen, um die Anwendung zu hängen, wenn ich auf den Stop-Button klicke - ich bin nicht sicher, wohin ich von hier aus gehen soll.

Meine Probe wird wahrscheinlich nicht gut geschrieben. Ich möchte ein besseres Verständnis von Multithreading, Deadlocks usw. bekommen. Ich habe mich in der Vergangenheit ein wenig mit BackgroundWorker beschäftigt, mache aber Java die meiste Zeit der letzten 2+ Jahre.

Form1.cs

public delegate void SetTextDelegate(string text); 

public partial class Form1 : Form 
{ 
    private Thread[] _slow; 
    private object lockTextBox = new object(); 

    public Form1() 
    { 
     InitializeComponent(); 
    } 

    #region Event Handlers 

    private void ui_btnClose_Click(object sender, EventArgs e) 
    { 
     this.Close(); 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 

    } 

    private void ui_btnStart_Click(object sender, EventArgs e) 
    { 
     if (_slow != null) 
     { 
      StopAllThreads(); 
     } 

     _slow = new Thread[ (int) numNoOfTheads.Value ]; 
     for(int i = 0; i < numNoOfTheads.Value; i++) 
     { 
      _slow[i] = new Thread(ThreadRunLoop); 
      _slow[i].Start(); 
     } 
    } 

    private void Form1_FormClosing(object sender, FormClosingEventArgs e) 
    { 
     if (_slow != null) 
     { 
      StopAllThreads(); 
     } 
    } 

    private void ui_btnStop_Click(object sender, EventArgs e) 
    { 
     if (_slow != null) 
     { 
      StopAllThreads(); 
     } 
    } 

    private void ui_btnClear_Click(object sender, EventArgs e) 
    { 
     this.textBox1.Clear(); 
    } 

    #endregion 

    protected void ThreadRunLoop() 
    { 
     try 
     { 
      for (int i = 0; i < 10000; i++) 
      { 
       UpdateText("Loop " + i + " for " + Thread.CurrentThread.ManagedThreadId); 
      } 
     } 
     catch (ThreadInterruptedException ex) 
     { 
      Console.WriteLine("Thread has been interrupted."); 
     } 
    } 

    private void UpdateText(string text) 
    { 
     //lock (lockTextBox) 
     //{ 
      if (textBox1.InvokeRequired) 
      { 
       textBox1.Invoke(new SetTextDelegate(UpdateText), text); 
      } 
      else 
      { 
       textBox1.SuspendLayout(); 
       textBox1.Text = textBox1.Text + text + System.Environment.NewLine; 
       textBox1.SelectionStart = textBox1.Text.Length; 
       textBox1.ScrollToCaret(); 
       textBox1.ResumeLayout(); 

      } 
     //} 
    } 

    private void StopAllThreads() 
    { 
     for (int i = 0; i < _slow.Length; i++) 
     { 
      if (_slow[i] != null) 
      { 
       _slow[i].Interrupt(); 
       _slow[i] = null; 
      } 
     } 
     _slow = null; 
    } 
} 

Form1.Designer.cs

partial class Form1 
{ 
    /// <summary> 
    /// Required designer variable. 
    /// </summary> 
    private System.ComponentModel.IContainer components = null; 

    /// <summary> 
    /// Clean up any resources being used. 
    /// </summary> 
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> 
    protected override void Dispose(bool disposing) 
    { 
     if (disposing && (components != null)) 
     { 
      components.Dispose(); 
     } 
     base.Dispose(disposing); 
    } 

    #region Windows Form Designer generated code 

    /// <summary> 
    /// Required method for Designer support - do not modify 
    /// the contents of this method with the code editor. 
    /// </summary> 
    private void InitializeComponent() 
    { 
     this.ui_btnClose = new System.Windows.Forms.Button(); 
     this.ui_btnStart = new System.Windows.Forms.Button(); 
     this.ui_btnStop = new System.Windows.Forms.Button(); 
     this.textBox1 = new System.Windows.Forms.TextBox(); 
     this.ui_btnClear = new System.Windows.Forms.Button(); 
     this.numNoOfTheads = new System.Windows.Forms.NumericUpDown(); 
     this.label1 = new System.Windows.Forms.Label(); 
     ((System.ComponentModel.ISupportInitialize)(this.numNoOfTheads)).BeginInit(); 
     this.SuspendLayout(); 
     // 
     // ui_btnClose 
     // 
     this.ui_btnClose.Location = new System.Drawing.Point(433, 268); 
     this.ui_btnClose.Name = "ui_btnClose"; 
     this.ui_btnClose.Size = new System.Drawing.Size(75, 23); 
     this.ui_btnClose.TabIndex = 0; 
     this.ui_btnClose.Text = "Close"; 
     this.ui_btnClose.UseVisualStyleBackColor = true; 
     this.ui_btnClose.Click += new System.EventHandler(this.ui_btnClose_Click); 
     // 
     // ui_btnStart 
     // 
     this.ui_btnStart.Location = new System.Drawing.Point(12, 12); 
     this.ui_btnStart.Name = "ui_btnStart"; 
     this.ui_btnStart.Size = new System.Drawing.Size(75, 23); 
     this.ui_btnStart.TabIndex = 1; 
     this.ui_btnStart.Text = "Start"; 
     this.ui_btnStart.UseVisualStyleBackColor = true; 
     this.ui_btnStart.Click += new System.EventHandler(this.ui_btnStart_Click); 
     // 
     // ui_btnStop 
     // 
     this.ui_btnStop.Location = new System.Drawing.Point(12, 41); 
     this.ui_btnStop.Name = "ui_btnStop"; 
     this.ui_btnStop.Size = new System.Drawing.Size(75, 23); 
     this.ui_btnStop.TabIndex = 2; 
     this.ui_btnStop.Text = "Stop"; 
     this.ui_btnStop.UseVisualStyleBackColor = true; 
     this.ui_btnStop.Click += new System.EventHandler(this.ui_btnStop_Click); 
     // 
     // textBox1 
     // 
     this.textBox1.Location = new System.Drawing.Point(93, 12); 
     this.textBox1.Multiline = true; 
     this.textBox1.Name = "textBox1"; 
     this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Both; 
     this.textBox1.Size = new System.Drawing.Size(415, 241); 
     this.textBox1.TabIndex = 3; 
     // 
     // ui_btnClear 
     // 
     this.ui_btnClear.Location = new System.Drawing.Point(352, 268); 
     this.ui_btnClear.Name = "ui_btnClear"; 
     this.ui_btnClear.Size = new System.Drawing.Size(75, 23); 
     this.ui_btnClear.TabIndex = 4; 
     this.ui_btnClear.Text = "Clear"; 
     this.ui_btnClear.UseVisualStyleBackColor = true; 
     this.ui_btnClear.Click += new System.EventHandler(this.ui_btnClear_Click); 
     // 
     // numNoOfTheads 
     // 
     this.numNoOfTheads.Location = new System.Drawing.Point(12, 98); 
     this.numNoOfTheads.Name = "numNoOfTheads"; 
     this.numNoOfTheads.Size = new System.Drawing.Size(74, 20); 
     this.numNoOfTheads.TabIndex = 5; 
     this.numNoOfTheads.Value = new decimal(new int[] { 
     1, 
     0, 
     0, 
     0}); 
     // 
     // label1 
     // 
     this.label1.AutoSize = true; 
     this.label1.Location = new System.Drawing.Point(9, 82); 
     this.label1.Name = "label1"; 
     this.label1.Size = new System.Drawing.Size(83, 13); 
     this.label1.TabIndex = 6; 
     this.label1.Text = "No. Of Threads:"; 
     // 
     // Form1 
     // 
     this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 
     this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 
     this.ClientSize = new System.Drawing.Size(520, 303); 
     this.Controls.Add(this.label1); 
     this.Controls.Add(this.numNoOfTheads); 
     this.Controls.Add(this.ui_btnClear); 
     this.Controls.Add(this.textBox1); 
     this.Controls.Add(this.ui_btnStop); 
     this.Controls.Add(this.ui_btnStart); 
     this.Controls.Add(this.ui_btnClose); 
     this.Name = "Form1"; 
     this.Text = "Slow My Machine"; 
     this.Load += new System.EventHandler(this.Form1_Load); 
     this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing); 
     ((System.ComponentModel.ISupportInitialize)(this.numNoOfTheads)).EndInit(); 
     this.ResumeLayout(false); 
     this.PerformLayout(); 

    } 

    #endregion 

    private System.Windows.Forms.Button ui_btnClose; 
    private System.Windows.Forms.Button ui_btnStart; 
    private System.Windows.Forms.Button ui_btnStop; 
    private System.Windows.Forms.TextBox textBox1; 
    private System.Windows.Forms.Button ui_btnClear; 
    private System.Windows.Forms.NumericUpDown numNoOfTheads; 
    private System.Windows.Forms.Label label1; 
} 

aktualisieren Wenn ich die Sperre auf die sonst in der Updateverfahren zu bewegen und einen Thread hinzufügen .Schlaf (20); in der Schleife, dann ist meine GUI reaktionsfähiger und ich kann auf die Schaltfläche Stop klicken und das Formular verschieben.

Jedes Feedback wird Behebungen geschätzt.

+0

könnte eine schlechte Antwort sein oder überhaupt keine Antwort, warum nicht die Update-Funktion synchronisiert? –

+0

Immer noch der gleiche Effekt mit vielen Threads. – Andez

+0

OK, also machen Sie blockierende Anrufe, deshalb friert die Benutzeroberfläche ein und führt einen asynchronen Anruf bei der blockierenden Funktion aus. –

Antwort

0

Versuchen Sie, die Sperre in UpdateText in den else zu verschieben.

+0

Versuchte, aber die Benutzeroberfläche reagiert nicht mehr und Sie können das Formular nicht verschieben oder auf die Schaltfläche Stopp klicken. – Andez

+0

Ich kann ein Thread.Sleep (20) in der Schleife hinzufügen, die das Formular reaktionsfähiger macht. Das funktioniert ok für 2 Threads, aber wenn ich 8 habe, bin ich zurück zum Hauptproblem mit dem Formular reagiert nicht. – Andez

+0

in diesem Fall, 'Application.DoEvents();' könnte eine bessere Wette sein als 'Thread.Sleep();' – paul

0

Verwenden einer Background die den UI-Thread aktualisiert.

Hier ist ein gutes Beispiel dafür, wie die Background verwenden:

http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

Es erklärt nicht, wie die Daten (Ihre int value) und es zurückzuholen in einem Textfeld setzen, aber es ist ein guter Anfang .

1

Ändern der for-Schleife zu

for (int i = 0; i < 10000; i++) 
    { 
     var text = "Loop " + i + " for " + Thread.CurrentThread.ManagedThreadId; 
     if (textBox1.InvokeRequired) 
      textBox1.Invoke(new SetTextDelegate(UpdateText), text); 
     else 
      UpdateText(text); 
    } 

Und das Update

ändern
private void UpdateText(string text) 
    { 
     textBox1.SuspendLayout(); 
     textBox1.Text = textBox1.Text + text + System.Environment.NewLine; 
     textBox1.SelectionStart = textBox1.Text.Length; 
     textBox1.ScrollToCaret(); 
     textBox1.ResumeLayout(); 
    } 

EDIT: Mein Fehler. Dies wird nur die Organisation verbessern und nicht in irgendeinem Aspekt. Wenn Sie die Benutzeroberfläche so häufig aktualisieren möchten, sollten Sie BackgroundWorker verwenden, was rdkleine sagte.

+0

Nicht so sicher, wie das tut mir leid. Es bewegt nur den UpdateText-Aufruf in die Schleife? – Andez

+0

@Andez Sorry, es ist mein Fehler. Ich habe die Antwort modifiziert. – Prakash

2

Die lock innerhalb UpdateText eine Sackgasse führen. Der Worker-Thread ruft die Sperre auf und ruft dann Invoke auf. Der dann aufgerufene UI-Thread versucht, die gleiche Sperre zu erhalten, muss aber warten, bis sie freigegeben wird. Die Sache ist, dass die Sperre niemals freigegeben wird, da Invoke blockiert, bis der UI-Thread die Ausführung des Delegaten beendet hat. Das passiert nie, weil der UI-Thread immer noch darauf wartet, die Sperre zu erhalten. Sackgasse!

+0

Danke für diese Information Brian +1 – Andez