我创建了一个简单的WindowsForms-Application,其中包含一个button
和一个label
。
如果单击按钮,则标签应显示1到100000之间的数字。
但是,如果我点击button
,那么GUI
会冻结,直到程序计数到100000,然后标签显示100000,GUI停止冻结。
从1到100000的计数在一个新线程(不是GUI线程)中执行,然后用labeltext
更改BeginInvoke
,但它不起作用..
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
new Thread(DoSomethingExpensive).Start();
}
void DoSomethingExpensive() {
for (int i = 0; i < 100000; i++) {
this.label1.BeginInvoke((Action)(() => {
label1.Text = "" + i;
}
));
}
}
}
}
答案 0 :(得分:2)
BeginInvoke
是异步的。这意味着它将操作排队到UI线程中,然后继续其业务。在UI线程中排队100,000个动作实际上并不需要很长时间。事实上,它比实际执行每个动作花费的时间要少得多。这意味着队列最终充满了所有这些请求以更新文本。当添加新请求时,例如,重新绘制表单以显示新值的请求,或者响应表单上的鼠标单击事件时,在数千个其他请求之后,它将被放置在队列的末尾。
如果您使用Invoke
而不是BeginInvoke
,则后台工作人员不会继续排队下一个项目,直到上一个项目在UI线程中运行完毕,保留它从领先UI线程并充斥队列。这意味着任何其他UI事件(例如实际重新绘制屏幕的事件)都不会超过队列中最多一个项目等待。
答案 1 :(得分:0)
对于每个值,应该进行线程上下文切换。如果你这样做100000次,UI线程就会冻结。这是由asynchronouse调用引起的。除了EXECUTE这个委托之外,UI线程没有其他功能。用户界面没有时间从MessagePump unitil获取工作项,调用最后一个委托。 MessagePump通过WindowMessages触发控件的更新。
现在最后一个delgate的值设置为控件。
答案 2 :(得分:0)
这是一种天真的ReactiveUI方式:
public partial class Form1 : Form
{
IDisposable subscription;
IObservable<long> sequence;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (subscription != null)
subscription.Dispose();
sequence = Observable
.Interval(TimeSpan.FromMilliseconds(1))
.Take(10000); // generate a timed sequence
subscription = sequence // act upon the sequence
.ObserveOn(SynchronizationContext.Current)
.Subscribe(x => label1.Text = x.ToString());
}
}
取决于当前的reactiveui-winforms包