惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Visual Studio Blog
J
Java Code Geeks
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
Docker
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 聂微东
MyScale Blog
MyScale Blog
H
Help Net Security
Last Week in AI
Last Week in AI
T
The Blog of Author Tim Ferriss
M
MIT News - Artificial intelligence
大猫的无限游戏
大猫的无限游戏
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
P
Proofpoint News Feed
博客园 - 叶小钗
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
F
Fortinet All Blogs
Martin Fowler
Martin Fowler
Microsoft Security Blog
Microsoft Security Blog
T
Tailwind CSS Blog
aimingoo的专栏
aimingoo的专栏

博客园 - 喝咖啡就蒜瓣儿

通过六段代码理解join、wait、sleep、yield、endInvoke、Waitone的区别 JWT(JSON Web Tokens )简洁说明 通道、选区和蒙版:写给Ps初学者的参考 数据库中Schema(模式)概念的理解 怎样测试串口和串口线是否正常 DataTable如何删除特定行 Difference between WCF and Web API and WCF REST and Web Service C#创建带参数的线程 .NET Framework 工具 【转】实现Sqlite datediff日期时间相减的方法 Devexpress GridView增加CheckBox列 没有注册类 (异常来自 HRESULT:0x80040154 (REGDB_E_CLASSNOTREG)) 如何让Advanced Installer卸载软件时保留一些文件 【转】SQLite提示database disk image is malformed的解决方法 【转】Winform下KeyDown,KeyPress,KeyUp事件的总结 【转】C# 子窗体如何调用父窗体的方法 C# WinForm自定义控件响应键盘事件 DataTable中如何去除重复的项【转】 TAG的用法和用途[转] Sqlite: unable to open database file
Application.DoEvents()和多线程
喝咖啡就蒜瓣儿 · 2015-08-14 · via 博客园 - 喝咖啡就蒜瓣儿

首先将以下代码放到Button事件里面:

private void btnStart_Click(object sender, EventArgs e)
{
    for (int q = 0; q < 100000; q++)
    {
        textBox1.Text = q.ToString();
    }
}

你会发现当点击Start按钮后,循环会一直进行,此时窗体会出现假死的状态,如:无法拖动。直到循环结束,textBox1中才会显示出结果。如何解决窗体的假死状态?修改以上代码如下:

private void btnStart_Click(object sender, EventArgs e)
{
    for (int q = 0; q < 100000; q++)
    {
        textBox1.Text = q.ToString();
        //实时响应文本框中的值
        Application.DoEvents();
    }
}

此时再次点击Start按钮后,textBox中的数字会不断改变,同时,你也可以拖动窗体......

但是这样使用Application.DoEvents()好吗?如果用多线程来实现相同的效果呢?多线程实现代码如下: 

public Form1()
{
    InitializeComponent();
    //不捕获跨线程调用引起的异常
    CheckForIllegalCrossThreadCalls = false;
}

private void btnStart_Click(object sender, EventArgs e)
{
    Thread s1 = new Thread(new ThreadStart(ThreadMethod));
    s1.Start();
}

public void ThreadMethod()
{
    for (int q = 0; q < 100000; q++)
    {
        textBox1.Text = q.ToString();
    }
}

注意Form1()里面添加了一行代码"CheckForIllegalCrossThreadCalls = false;",解决跨线程调用产生的异常。

以上代码和使用Application.DoEvents()达到相同的效果。

下面我们来比较一下,使用Application.DoEvents()和使用多线程哪个更耗时?代码如下:

public void ThreadMethod()
{
    label1.Text = DateTime.Now.ToString();
    for (int q = 0; q < 100000; q++)
    {
        textBox1.Text = q.ToString();
    }
    label2.Text = DateTime.Now.ToString();
}

private void btnStartDoEvent_Click(object sender, EventArgs e)
{
    label3.Text = DateTime.Now.ToString();
    for (int q = 0; q < 100000; q++)
    {
        textBox2.Text = q.ToString();
        Application.DoEvents();//实时响应文本框中的值
    }
    label4.Text = DateTime.Now.ToString();
}

(假设以上比较时间的代码处在正确的位置)由此可知,Application.DoEvents()消耗的时间更少,但这里并不建议使用Application.DoEvents(),因为它会引起很多未知的错误。 

补充:由于显示的定义"CheckForIllegalCrossThreadCalls = false"并不是好的方法,下面来改进一下,如下:

int i;

private void btnStart_Click(object sender, EventArgs e)
{
    Thread s1 = new Thread(new ThreadStart(ThreadMethod));
    s1.Start();
}

private void ThreadMethod()
{
    for (i = 0; i < 100000; i++)
    {
        MethodInvoker mi = new MethodInvoker(Count);
        this.Invoke(mi);
        //BeginInvoke(mi);
    }
}

private void Count()
{
    textBox1.Text = i.ToString();
}

 MethodInvoker实质上是一个委托,查看其定义可知......

using System;                                                                                           

namespace System.Windows.Forms 
{
    // 表示一个委托,该委托可执行托管代码中声明为 void 且不接受任何参数的任何方法。
    public delegate void MethodInvoker();
} 

以上用匿名委托的方式似乎更简洁,代码如下:

private void btnStart_Click(object sender, EventArgs e)
{
    new Thread((ThreadStart)(delegate ()
    {
        for (int i = 0; i < 10000; i++)
        {
            label1.Invoke((MethodInvoker)delegate ()
            {
                textBox1.Text = i.ToString();
            });
        };
    })).Start();
}

在次线程上计算,在主线程上调用label。