2013-01-28 8 views
5

Ich versuche, Threads zu verwenden und zu verhindern, dass das Programm einfriert, während der Thread beschäftigt ist. Es sollte den Fortschritt anzeigen (Schreiben von Nullen/1) und nicht nur das Ergebnis nach dessen Beendigung zeigen, sondern das Formular in der Zwischenzeit einfrieren.C# Threading mit Aufruf, Einfrieren des Formulars

In dem aktuellen Programm versuche ich in ein Textfeld zu schreiben, und tatsächlich einen konstanten Fortschritt zu sehen, und das Formular kann nicht durch die Aufgaben des anderen Threads beeinflusst werden.

Was ich jetzt habe ist, kann ich in ein Textfeld mit einem Thread schreiben mit invoke, aber es zeigt nur das Ergebnis (Form friert, während Thread beschäftigt ist), und das Formular friert ein.

Formular Bild:

enter image description here

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 MultiThreading 
{ 
public partial class MultiThreading : Form 
{ 
    public MultiThreading() 
    { 
     InitializeComponent(); 
    } 

    Thread writeOne, writeTwo; 

    private void writeText(TextBox textBox, string text) 
    { 
     if (textBox.InvokeRequired) 
     { 
      textBox.BeginInvoke((MethodInvoker)delegate() 
      { 
       for (int i = 0; i < 500; i++) 
       { 
        textBox.Text += text; 
       } 
      }); 
     } 
     else 
     { 
      for (int i = 0; i < 500; i++) 
      { 
       textBox.Text += text; 
      } 
     } 
    } 
    private void btnWrite1_Click(object sender, EventArgs e) 
    { 
     writeOne = new Thread(() => writeText(txtOutput1, "0")); 
     writeOne.Start(); 
    } 

    private void btnWrite2_Click(object sender, EventArgs e) 
    { 
     writeTwo = new Thread(() => writeText(txtOutput2, "1")); 
     writeTwo.Start(); 
    } 

    private void btnClear1_Click(object sender, EventArgs e) 
    { 
     txtOutput1.Clear(); 
    } 

    private void btnClear2_Click(object sender, EventArgs e) 
    { 
     txtOutput2.Clear(); 
    } 

    private void btnWriteBoth_Click(object sender, EventArgs e) 
    { 
     writeOne = new Thread(() => writeText(txtOutput1, "0")); 
     writeTwo = new Thread(() => writeText(txtOutput2, "1")); 

     writeOne.Start(); 
     writeTwo.Start(); 
    } 

    private void btnClearBoth_Click(object sender, EventArgs e) 
    { 
     txtOutput1.Clear(); 
     txtOutput2.Clear(); 
    } 
} 

} 

EDIT:

Btw für jedermann, ich bin neu zu Multithreading fragen, und ich versuche nur, ein kleines Programm zu schreiben, das zu verstehen, beste Möglichkeit, dies zu tun.

Ich verstehe, dass mein vorheriger Aufruf nicht wirklich geholfen hat, weil ich dem Formular noch immer keine Chance gab, es zu aktualisieren, also kam es dorthin.

Ok, so läuft 1 Thread wie dieser funktioniert, aber immer noch mehrere Threads zusammen laufen, wird das Formular nicht aktualisieren, bis nach dem Thread fertig ist.
Ich habe eine thread.sleep() hinzugefügt, so dass ich versuchen kann, während des Schreibens zu löschen, um zu sehen, ob ich das Formular noch verwenden kann.

Wenn ich in 1 Textbox schreibe, kann ich beim Schreiben immer noch den Bildschirm löschen.
Aber sobald ich 2 Threads verwende, kann ich das Formular nicht mehr verwenden, bis der Thread abgeschlossen ist, und gibt die Ausgabe.

private void writeText(TextBox textBox, string text) 
    { 
     for (int i = 0; i < 500; i++) 
     { 
      Invoke(new MethodInvoker(() => 
      { 
       textBox.Text += text; 
       Thread.Sleep(2); 
      })); 
     } 

    } 

(Wenn ich auf diese völlig falsch bin ich nicht dagegen durch einige Beispiele/Threads zu lesen ist, versuche ich immer noch zu sehen, was der beste Weg, dies zu tun, neben einem Background)

EDIT 2:

ich habe die Anzahl der Invokes reduziert durch Verringerung der Menge zu schreiben, aber Verzögerung zu erhöhen, um die gleiche Wirkung der ständigen schreiben geben, nur um die Last zu verringern.

private void writeText(TextBox textBox, string text) 
    { 
     for (int i = 0; i < 500; i++) 
     { 
      Invoke(new MethodInvoker(() => 
      { 
       textBox.Text += text; 
       Thread.Sleep(2); 
      })); 
     } 

    } 

EDIT 3:

Sumeet Das Beispiel arbeitet

Application.DoEvents mit();

(die s bemerken, ist .DoEvent nicht, Tippfehler wahrscheinlich: P), mehrere Saiten gleichzeitig & Schreiben sie den Fortschritt zeigen, die nicht nur das Ergebnis.

So aktualisieren-Code wieder :)

* eine neue Schaltfläche Mit 5 Verknüpfungen zu erstellen, die eine Zufallszahl an den beiden Textfelder

private void writeText(TextBox textBox, string text) 
    { 
     for (int i = 0; i < 57; i++) 
     { 
      Invoke(new MethodInvoker(() => 
      { 
       textBox.Text += text; 
       Thread.Sleep(5); 
       Application.DoEvents(); 
      })); 
     } 

    } 
private void btnNewThread_Click(object sender, EventArgs e) 
    { 
     Random random = new Random(); 
     int[] randomNumber = new int[5]; 
     for (int i = 0; i < 5; i++) 
     { 
      randomNumber[i] = random.Next(2, 9); 
      new Thread(() => writeText(txtOutput1, randomNumber[i-1].ToString())).Start(); 
      new Thread(() => writeText(txtOutput2, randomNumber[i-1].ToString())).Start(); 
     } 
    } 
+3

Betrachten Sie mit [ 'BackgroundWorker'] (http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx), wenn Sie einen langlaufende Prozess läuft haben im Hintergrund und die Benutzeroberfläche muss reaktionsfähig bleiben. –

+0

@RobertHarvey Das würde diesem bestimmten Beispiel nicht wirklich helfen. – Servy

+1

@Servy Dieses spezielle Beispiel scheint ... kaputt zu sein. Und erfunden. Es gibt einen ausgetretenen Pfad für diese Art von Dingen. –

Antwort

6

Diese Lösung funktioniert! Habe es überprüft.

Das Problem ist, dass Sie dem UI-Thread immer wieder sagen, dass er den Text ändern soll, aber nie Zeit haben soll, Ihnen den aktualisierten Text anzuzeigen.

textBox.Text += text; 
Application.DoEvents(); 

P. S.: Um Ihre UI zeigen den geänderten Text, fügen Sie die Application.DoEvents Zeile wie diese zu machen : Entferne den Else-Block deiner If/Else-Schleife, er ist redundant, und wie von anderen darauf hingewiesen, gibt es keine Verwendung zum Erstellen dieser 2 Threads, da sie nur die Nachricht auf dem UI-Thread selbst veröffentlichen.

+0

Das funktioniert in der Tat, jetzt habe ich keine Verzögerung von den Threads, sogar mehrere Threads laufen;) –

+2

Ich wäre vorsichtig mit 'DoEvents()'. Verwenden Sie es sparsam; Ich habe gesehen, wie Deadlock-Programme von Leuten willkürlich verwendet werden. Wenn Sie ein Threading durchführen, sollten Sie selten 'DoEvents()' benötigen. –

+1

Wenn Sie mich fragen, sollten Sie ** Application.DoEvents() '** niemals ** verwenden. Es ist ... in dieser Situation überschaubar, aber es ist keine Gewohnheit, an die Stelle von richtigem Multithreading zu kommen. – JosephHirn

2

Sie besiegte den Zweck der Verwendung von Threads schreiben.

Alles, was Ihr Thread tut, ist der UI-Thread zu sagen, um Code mit BeginInvoke() auszuführen.

Die gesamte tatsächliche Arbeit geschieht auf dem UI-Thread.

+0

Dies erklärt das Problem, aber diskutiert nicht, wie es zu lösen ist. – Servy

5

Sie führen immer noch eine Singlethread-Aufgabe aus und starten sie bei Bedarf einfach erneut auf dem UI-Thread.

for (int i = 0; i < 500; i++){ 
    string text = ""+i; 
    textBox.BeginInvoke((MethodInvoker)delegate() 
      { 
       textBox.Text += text; 
      }); 
} 
+0

+1 Der OP gibt den UI-Thread während der Schleife nie frei. – usr

3

Das Problem ist, dass Sie einen neuen Thread fängst, und dann das neue Thread außer Nichtstun eine neue Aufgabe für den UI-Thread-Prozess hinzufügen, die viel Arbeit macht. Um Ihr Formular reaktionsschnell zu halten, müssen Sie Zeit haben, wo der UI-Thread nichts tut, oder zumindest nicht viel Zeit für eine Aufgabe aufwenden.

Um die Form ansprechend zu halten, müssen wir viele kleine BeginInvoke (oder Invoke) Anrufe haben.

private void writeText(TextBox textBox, string text) 
{ 
    for (int i = 0; i < 500; i++) 
    { 
     Invoke(new MethodInvoker(() => 
     { 
      textBox.Text += text; 
     })); 
    } 
} 

Durch viele kleine invoke mit nennt es ermöglicht Dinge wie Farbe Ereignisse, Maus verschieben/Klicken Veranstaltungen etc. in der Mitte der Operationen behandelt werden. Beachten Sie auch, dass ich den Anruf InvokeRequired entfernt habe. Wir wissen, dass diese Methode von einem Nicht-UI-Thread aufgerufen wird, so dass es keine Notwendigkeit dafür gibt.

0

Entweder Sie Datenverarbeitung oder Sie versuchen nur, die Benutzeroberfläche zu animieren.

Für die Datenverarbeitung sollten Sie alle schweren Arbeiten an einem Hintergrund-Thread durchführen und nur gelegentlich die Benutzeroberfläche aktualisieren. In Ihrem Beispiel ist eine TextBox in dieser Hinsicht besonders problematisch, da Sie dem zugrunde liegenden Datenmodell mehrere hundert Mal Daten hinzufügen und das UI-Element (eine TextBox) jedes Mal länger zum Rendern benötigt. Sie müssen vorsichtig sein, wie oft die UI aktualisiert werden soll, damit die Verarbeitung von UI-Aktualisierungen Datenmodellaktualisierungen nicht überlastet. TextBoxes sind so böse.

Im Beispiel unten, wird ein Flag während des Paint-Ereignis gesetzt wird sichergestellt, dass zusätzliche UI-Updates nicht die Warteschlange gestellt werden, bis die TextBox das letzte Update Malerei beendet hat:

string str = string.Empty; 
public void DoStuff() 
{ 
    System.Threading.ThreadPool.QueueUserWorkItem(WorkerThread); 
} 

void WorkerThread(object unused) 
{ 
    for (int i = 0; i < 1000; i++) 
    { 
     str += "0"; 
     if (updatedUI) 
     { 
      updatedUI = false; 
      BeginInvoke(new Action<string>(UpdateUI), str); 
     } 
    } 
    BeginInvoke(new Action<string>(UpdateUI), str); 
} 

private volatile bool updatedUI = true; 
void textbox1_Paint(object sender, PaintEventArgs e) // event hooked up in Form constructor 
{ 
    updatedUI = true; 
} 

void UpdateUI(string str) 
{ 
    textBox1.Text = str; 
} 

Auf der anderen Seite, wenn UI Animation Ihr Ziel sollte dann wahrscheinlich etwas anderes als eine TextBox sein. Es ist einfach nicht so konzipiert, um Updates so häufig zu behandeln. Es könnte einige Optimierungen für das Text-Rendering geben, das Sie für Ihren speziellen Anwendungsfall machen könnten.

+0

Im Moment versuche ich nur einen guten Überblick über Multithreading zu haben. Ich benutze die Textbox, weil es einfach zu sehen ist, dass Sie ständig in die Textbox schreiben und dass ich das Formular immer noch verwenden kann. Im Moment versuche ich nur, konstant in die Textbox zu schreiben, wo Sie den Fortschritt sehen und auch andere Aufgaben gleichzeitig erledigen können und keinen Thread starten, nur das Ergebnis sehen und das Formular nicht verwenden können . –

0

Sie dürfen niemals eine Zeichenfolge in Anwendungen mit hohem Volumen verwenden. UI oder nicht. Multi-Threading oder nicht.

Sie sollten StringBuilder verwenden, um die Zeichenfolge zu akkumulieren. und weisen Sie dann

tb.Text = sb.ToString(); 
+0

Mmm .... Jemand muss deine Antwort bestätigen ... – kokbira