C # Updating TextBox from multiple threads

I put together a simple WinForm that would spawn a series of threads to loop from 0 to 10000 - the goal of this is to slow down Windows so that some other programs run slowly.

Basically the form has a textbox in which I want to write the loop index from each thread. It was ok for one thread, but since I presented more threads, I would hang the app when I hit the Stop button - I'm not too sure where to go from here.

My example is probably poorly written. I want to better understand multithreading, deadlocks, etc. I've done BackgroundWorker before, but have been doing Java for most of the past two plus years.

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 If I move the lock to else in the UpdateText method and add Thread.Sleep (20); in a loop, then my GUI is more responsive and I can hit the Stop button and move the form around.

Any feedback, corrections would be appreciated.

+3


source to share


4 answers


lock

inside UpdateText

will lead to a dead end. The worker thread acquires the lock and then calls Invoke

. The UI thread then makes attempts to acquire the same lock, but has to wait for it to be released. The point is that the lock will never be released, because it will Invoke

block until the UI thread has finished executing the delegate. This never happens because the UI thread is still waiting to acquire the lock. Dead end!



+2


source


Change the for loop to

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

      

And change UpdateText to



    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 : my mistake. It will just improve the organization, not in any aspect. If you want to update the interface frequently, you should use the BackgroundWorker , which is what rdkleine said.

+1


source


Try to move the lock to UpdateText so that inside else.

0


source


Use BackgroundWorker, which updates the UI thread.

Here's a good example of using BackgroundWorker:

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

It doesn't explain how to fetch data (int value) and put it in a textbox, but it's a good start.

0


source







All Articles