C#使用调用线程,冻结表单

我正在尝试使用线程并防止程序在线程繁忙时冻结。 它应该显示进度(写入0/1)并且不仅仅是在完成后显示结果,同时冻结表格。

在当前程序中,我正在尝试写入文本框,实际上看到不断进展,并且表单不会受到其他线程任务的影响。

我现在拥有的是我可以使用调用写入带有线程的文本框,但它只显示结果(表单在线程繁忙时冻结),并且表单冻结。

表格图片:

在此处输入图像描述

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

编辑:

对于任何想知道的人来说,我是multithreading的新手,我只是想编写一个小程序来了解最好的方法。

我知道我以前的调用并没有真正帮助,因为我仍然没有给表单提供更新的机会,所以它到达那里。

好的,这样运行1个这样的线程,但仍然在一起运行多个线程,直到线程完成后才会更新表单。
我添加了一个thread.sleep(),所以我可以在写作时尝试清除,看看我是否仍然可以使用该表单。

写入1个文本框时,我仍然可以在写入时清除屏幕。
但是一旦我使用2个线程,我就不能再使用该表单直到线程完成,并给出输出。

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

(如果我对此完全错误,我不介意阅读一些示例/线程,我仍然试图看看除了背景工作者之外,最好的方法是什么)

编辑2:

我通过减少写入量来减少调用次数,但是增加延迟会产生相同的持续写入效果,只是减少负载。

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

编辑3:

Sumeet的例子可以使用

Application.DoEvents();

(注意s,.DoEvent不起作用,可能是错字:P),同时写多个字符串并让它们显示进度而不仅仅是结果。

所以代码再次更新:)

*使用新按钮创建5个线程,将随机数写入两个文本框

 private void writeText(TextBox textBox, string text) { for (int i = 0; i  { 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  writeText(txtOutput1, randomNumber[i-1].ToString())).Start(); new Thread(() => writeText(txtOutput2, randomNumber[i-1].ToString())).Start(); } } 

此解决方案有效! 检查过了。

问题是你一直告诉UI线程改变文本,但永远不要让它有时间向你显示更新的文本。 要使UI显示已更改的文本,请添加Application.DoEvents行,如下所示:

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

ps:删除你的If / Else循环的else块,它是多余的,并且也像其他人指出的那样,没有任何使用创建这两个线程,因为它们正在做的就是在UI线程本身上发布消息。

您仍在执行单线程任务,只需在UI线程上重新启动它即可。

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

问题是你正在开始一个新的线程,然后新的线程什么都不做,除了为UI线程添加一个新的任务来处理它做了很多工作。 为了保持表单响应,您需要有时间在UI线程无效,或者至少不花费大量时间执行任何任务。

为了保持表单响应,我们需要进行大量的BeginInvoke (或Invoke )调用。

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

通过进行大量的小调用调用,可以在操作过程中处理绘制事件,鼠标移动/点击事件等事件。 另请注意,我删除了InvokeRequired调用。 我们知道这个方法将从非UI线程调用,因此不需要它。

你正在打败使用线程的目的。

你的所有线程都告诉UI线程使用BeginInvoke()执行一些代码。

所有实际工作都发生在UI线程上。

您要么正在进行数据处理,要么只是尝试为UI设置动画。

对于数据处理,您应该在后台线程上完成所有繁重工作,并且仅偶尔更新UI。 在您的示例中,TextBox在这方面特别麻烦,因为您将数据添加到底层数据模型数百次,并且UI元素(TextBox)每次渲染所需的时间更长。 您必须小心更新UI的频率,以便UI更新的处理不会超过数据模型更新。 TextBoxes很讨厌。

在下面的示例中,在paint事件期间设置的标志确保在TextBox完成绘制上次更新之前,其他UI更新不会排队:

 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(UpdateUI), str); } } BeginInvoke(new Action(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; } 

另一方面,如果UI动画是你的目标,那么你可能应该使用除TextBox之外的其他东西。 它并不是为了经常处理更新而设计的。 对于特定用例,可能会对文本呈现进行一些优化。

您绝不能在大批量应用程序中使用字符串。 UI与否。 multithreading与否。

您应该使用StringBuilder来累积字符串。 然后分配

 tb.Text = sb.ToString();