【问题标题】:C# Updating TextBox from multiple ThreadsC# 从多个线程更新文本框
【发布时间】:2012-03-29 10:34:52
【问题描述】:

我正在组合一个简单的 WinForm,它会生成多个线程以从 0 循环到 10000 - 这样做的目的 - 减慢 Windows 以使其他一些程序运行缓慢。

基本上,表单有一个文本框,我想从每个线程中写入循环索引。对于单个线程来说一切都很好,但是由于我引入了更多线程,所以当我单击“停止”按钮时,我似乎会挂起应用程序 - 我不太确定从这里去哪里。

我的示例可能写得不好。我想更好地了解多线程、死锁等。我过去曾涉足过 BackgroundWorker,但在过去 2 年多的时间里大部分时间都在使用 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;
}

更新 如果我在 UpdateText 方法中将锁移到 else 并添加一个 Thread.Sleep(20);在循环中,然后我的 GUI 响应更快,我可以单击停止按钮并移动表单。

任何反馈,修复将不胜感激。

【问题讨论】:

  • 可能是一个不好的答案或根本不是一个答案,你为什么不同步更新功能?
  • 用很多线程还是一样的效果。
  • 好的,所以你正在进行阻塞调用,这就是 UI 冻结的原因,对阻塞函数进行异步调用。

标签: c# multithreading user-interface


【解决方案1】:

UpdateText 中的lock 会导致死锁。工作线程获取锁,然后调用Invoke。 UI 线程然后调用尝试获取 same 锁,但必须等到它被释放。问题是锁永远不会被释放,因为Invoke 阻塞,直到 UI 线程完成执行委托。这永远不会发生,因为 UI 线程仍在等待获取锁。死锁!

【讨论】:

  • 感谢 Brian +1 提供的信息
【解决方案2】:

将for循环改为

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

编辑:我的错误。这只会改善组织,而不是任何方面。如果你想如此频繁地更新 UI,你应该使用 BackgroundWorker rdkleine 所说的。

【讨论】:

  • 不太清楚这有什么帮助抱歉。它只是将 UpdateText 调用移动到循环中?
  • @Andez 对不起,这是我的错误。我修改了答案。
【解决方案3】:

尝试将 UpdateText 中的锁移到 else 中。

【讨论】:

  • 试过了,但 UI 变得无响应,您无法移动表单或单击停止按钮。
  • 我可以在循环中添加一个 Thread.Sleep(20) 以使表单更具响应性。这适用于 2 个线程,但如果我有 8 个线程,我会回到表单无响应的主要问题。
  • 在这种情况下,Application.DoEvents(); 可能比Thread.Sleep(); 更好
  • 是的,我的错误 - UpdateText 无论如何都应该是单线程的,所以你不需要使用锁。
【解决方案4】:

使用更新 UI 线程的 BackgroundWorker。

这是一个如何使用 BackgroundWorker 的好例子:

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

它没有解释如何检索数据(您的 int 值)并将其放入文本框中,但这是一个好的开始。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多