【问题标题】:Why does a Thread.Sleep running in a Task not block the WinForms UI Thread?为什么在任务中运行的 Thread.Sleep 不会阻塞 WinForms UI 线程?
【发布时间】:2014-10-20 05:47:59
【问题描述】:

我目前正在使用 C# 和 Window Forms 中的任务,我遇到了一个奇怪的效果。 我有一个表格,其中包含一个每 300 毫秒计时一次的计时器。滴答事件将该表单中的控件的背景更改为随机颜色。我有另一个按钮,单击它会启动一个新任务,它只使用Thread.Sleep 等待 3 秒。我还插入了一个用于记录的文本框。

根据我对任务的了解,他们不会创建新线程来运行任务(日志也显示了这一点),我希望第一个按钮在任务运行时停止更改颜色 3 秒,因为一个线程一次只能做一件事。要么按钮闪烁,要么在 3 秒内什么都不做。

然而,这个假设似乎是错误的,因为即使线程应该处于休眠状态,按钮也会很高兴地改变它的颜色!这怎么可能?

跟进:我注意到从任务方法中,我必须使用Invoke 来访问日志文本框。但是,根据 MSDN 的Control.InvokeRequired 文档:

如果控件的句柄是在与调用线程不同的线程上创建的,则为真(表明您必须通过调用方法调用控件);否则为假。

既然这是一个单线程场景,InvokeRequired 怎么可能是真的?

P.S.:我知道Task.Delay 是一个东西。我想了解为什么 UI 线程在 Thread.Sleep 期间不会阻塞。

日志输出:

[T9] Before await
[T9] [I] Task Start
[T9] [I] Task End
[T9] After await

闪烁的按钮还显示了tick事件处理程序执行的线程的线程ID,也是9。

完整代码

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

namespace AsyncAwaitTest
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }

    public class Form1 : Form
    {
        private readonly Button _buttonFlash;
        private readonly System.Windows.Forms.Timer _timerFlash;
        private readonly TextBox _textLog;

        private readonly Random _rand = new Random();

        public Form1()
        {
            _buttonFlash = new Button();
            var buttonAwait = new Button();
            _timerFlash = new System.Windows.Forms.Timer();
            _textLog = new TextBox();
            SuspendLayout();

            _buttonFlash.Location = new Point(12, 12);
            _buttonFlash.Size = new Size(139, 61);

            buttonAwait.Location = new Point(213, 12);
            buttonAwait.Size = new Size(110, 61);
            buttonAwait.Text = "Wait Some Time";
            buttonAwait.Click += buttonAwait_Click;

            _timerFlash.Interval = 300;
            _timerFlash.Tick += TimerFlashTick;

            _textLog.Location = new Point(36, 79);
            _textLog.Multiline = true;
            _textLog.Size = new Size(351, 167);

            ClientSize = new Size(480, 286);
            Controls.Add(_textLog);
            Controls.Add(buttonAwait);
            Controls.Add(_buttonFlash);
            Text = "Form1";

            ResumeLayout(false);
            PerformLayout();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            _timerFlash.Start();
        }

        private void Log(string text)
        {
            if (InvokeRequired)
            {
                Invoke((Action<string>) Log, "[I] " + text);
                return;
            }
            _textLog.Text += string.Format("[T{0}] {1}{2}", Thread.CurrentThread.ManagedThreadId, text, Environment.NewLine);
        }

        private void TimerFlashTick(object sender, EventArgs e)
        {
            _buttonFlash.Text = Thread.CurrentThread.ManagedThreadId.ToString();
            _buttonFlash.BackColor = Color.FromArgb(255, _rand.Next(0, 255), _rand.Next(0, 255), _rand.Next(0, 255));
        }

        private async void buttonAwait_Click(object sender, EventArgs e)
        {
            Log("Before await");
            await Task.Factory.StartNew(Something);
            Log("After await");
        }

        private void Something()
        {
            Log("Task Start");
            Thread.Sleep(3000);
            Log("Task End");
        }
    }
}

【问题讨论】:

  • 请出示您的日志。我希望Something 在非 UI 线程中执行,这与“因为根据我对任务的理解,他们不会创建新线程来运行任务(日志也显示了这一点)”。请注意,仅当您记录线程 ID 时,您一定会在 UI 线程中...
  • 完成。我唯一能想象到的问题是Thread.CurrentThread.ManagedThreadId 在骗我。
  • 啊啊啊啊,我只是在调用UI线程后才将线程ID添加到日志消息中!这就解释了一切。
  • 要发现的是,您已经在其中找到了[I]...

标签: c# .net winforms asynchronous task


【解决方案1】:

根据我对任务的了解,他们不会创建新线程来运行任务

Task 不一定会创建一个新线程本身,但Task.Factory.StartNew 会。它使用当前的任务调度程序来调度任务执行,默认情况下从线程池中获取一个工作线程。

请阅读:

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

【讨论】:

    猜你喜欢
    • 2016-12-16
    • 2021-08-29
    • 2016-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多