2012-03-29 2 views
3

Я собирал простой WinForm, который породил бы ряд потоков для цикла от 0 до 10000 - цель этого - замедлить работу Windows, чтобы некоторые другие программы работали медленно.C# Обновление TextBox из нескольких потоков

В основном форма имеет текстовое поле, которое я хочу написать индексом цикла из каждого потока. Все было в порядке для одного потока, но, поскольку я представил больше потоков, я бы повесил приложение, когда я нажимаю кнопку «Стоп» - я не слишком уверен, куда идти отсюда.

Мой выбор, вероятно, не написан хорошо. Я хочу лучше понять многопоточность, взаимоблокировки и т. Д. Я раньше занимался BackgroundWorker, но занимался Java в течение большей части последних двух с лишним лет.

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; 
} 

Update Если переместить замок на другое в методе UPDATETEXT и добавить тему .Sleep (20); в цикле, тогда мой графический интерфейс более отзывчив, и я могу нажать кнопку «Стоп» и переместить форму.

Любая обратная связь, исправления будут оценены.

+0

может быть плохим ответом или вообще не отвечать, почему вы не синхронизировали функцию обновления? –

+0

Все тот же эффект с большим количеством потоков. – Andez

+0

ОК, поэтому вы делаете блокирующие вызовы, поэтому пользовательский интерфейс замерзает, выдает асинхронный вызов функции блокировки. –

ответ

0

Попробуйте переместить замок в UpdateText внутри внутри.

+0

Пробовал это, но пользовательский интерфейс не реагирует, и вы не можете переместить форму или нажать кнопку «Стоп». – Andez

+0

Я могу добавить Thread.Sleep (20) в цикл, который делает форму более отзывчивой. Это работает нормально для 2 потоков, но если у меня есть 8, я вернусь к основной проблеме, когда форма не отвечает. – Andez

+0

в этом случае 'Application.DoEvents();' может быть лучше, чем 'Thread.Sleep();' – paul

0

Используйте BackgroundWorker, который обновляет поток пользовательского интерфейса.

Вот хороший пример того, как использовать BackgroundWorker:

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

Это не объясняет, как получить данные (ИНТ значение) и поместить его в текстовое поле, но это хорошее начало ,

1

Измените цикл для

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); 
    } 

И изменить UPDATETEXT к

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: Моя ошибка. Это просто улучшит организацию, а не в любом аспекте. Если вы хотите часто обновлять интерфейс, вы должны использовать BackgroundWorker, что сказал rdkleine.

+0

Не слишком уверен, как это помогает. Это просто перемещение вызова UpdateText в цикл? – Andez

+0

@Andez Извините, это моя ошибка. Я изменил ответ. – Prakash

2

lock внутри UpdateText вызывает тупик. Рабочий поток получает блокировку, а затем вызывает Invoke. Затем поток пользовательского интерфейса вызывает попытки получить тот же самый, но он должен ждать, пока он не будет выпущен. Дело в том, что блокировка никогда не будет выпущена, потому что Invoke блокирует, пока поток пользовательского интерфейса не завершит выполнение делегата. Это никогда не происходит, потому что поток пользовательского интерфейса все еще ожидает получения блокировки. Тупик!

+0

Спасибо за этот бит информации Брайан +1 – Andez

Смежные вопросы