线程无论如何都会使Windows无法响应,但人们会抵制DoEvents()。如何同意这些事情?

时间:2014-08-14 16:44:57

标签: c# multithreading winforms doevents

不要使用DoEvents()。使用线程!

这个口头禅在互联网上漫游,包括SO。 Okey所以我创建了一个简短的概念证明,我试图只使用Threads。所以基本上按钮应该触发向下移动蓝框。

它在一个单独的线程中运行YET窗体没有响应(我无法移动它或再次单击该按钮),直到它完成向下移动。

问题是,我搞砸了什么?如果我不应该使用DoEvents(),那么呢? (如果您取消注释Application.DoEvents()行,它就会响应)。

CODE

using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

namespace doevents
{
    public partial class Main : Form
    {
        // Use static so that I don't have to pass them over and over
        public static TableLayoutPanel movingBox;
        public static Form mainForm;

        // Initialize
        public Main()
        {
            InitializeComponent();

            Main.mainForm = this;
            Main.movingBox = tableLayoutPanel1;
        }

        // Button that runs thread which will move the blue box down
        private void button1_Click(object sender, EventArgs e)
        {
            new Thread(
                new ThreadStart(
                    () => 
                        {
                            SlideBoxDown();
                        }
                    )
                ).Start();
        }

        // Delegate
        delegate void SlideBoxDownCallback();

        // Slide box down
        private static void SlideBoxDown()
        {
            if (Main.movingBox.InvokeRequired)
            {
                SlideBoxDownCallback d = new SlideBoxDownCallback(SlideBoxDown);
                Main.mainForm.Invoke(d, new object[] { });
            }
            else
            {
                for (int i = 0; i < 20; i++)
                {
                    Main.movingBox.Location = new Point(Main.movingBox.Location.X, Main.movingBox.Location.Y + 2);
                    Thread.Sleep(100);
                    //Application.DoEvents();
                }
            }
        }       
    }
}

应用程序布局

enter image description here

3 个答案:

答案 0 :(得分:3)

您仍然在主线程上调用Thread.Sleep(100)。因为SlideBoxDown正在将自己调用回GUI线程。由于这是Thread中唯一发生的事情,因此您的程序基本上是单线程的。

for (int i = 0; i < 20; i++)根本不是控制动画的好方法 使用计时器。

关于(如果取消注释Application.DoEvents()行,它会响应) - 正确,但请尝试在该动画中间关闭Form。你不会喜欢它。

答案 1 :(得分:1)

您创建一个新线程,然后在您从新线程调用的方法中,您将编组回UI线程,然后在UI线程中运行所有其余代码。这很好地打破了首先开始新线程的目的。

要每隔X个时间间隔执行一次操作,在这种情况下每100毫秒移动一个对象,请使用Timer

答案 2 :(得分:-1)

对应用程序UI的更改需要UI线程。这就是它的全部内容。您的代码正在创建后台线程,其唯一目的是将消息传递给应用程序的消息泵,该消息由UI线程使用。因此,“else”子句中发生的所有事情,包括Thread.Sleep()调用,都在主线程上执行。

我会重构这段代码,用Timer替换Sleep。 Timer将在后台线程上运行,并且可以在其“Tick”事件(间隔100ms)上触发处理程序,该事件可以将矩形移动一个增量。这样,绘图基于在UI线程上消费消息而发生,但是等待是由后台线程完成的,从而保持UI响应。