I am currently playing with Tasks in C # and Window Forms, and I came across some weird effect. I have a form that contains a timer that ticks every 300 ms. A tick event changes the background of the control in this form to a random color. I have another button that, when clicked, launches a new task that simply uses Thread.Sleep3 seconds to wait. I also added a text box for logging.
Since, from what I understand in Tasks, they don’t create new threads for starting tasks (and the log also shows this), I expect the first button to stop changing color for 3 seconds while the task is running, as the thread can do only one thing at once. Either the button blinks or does nothing for 3 seconds.
However, this assumption seems wrong, since the button will happily change its color, even if the stream is supposedly sleeping! How can it be?
Follow-up: I noticed that from the tasks method I have to use the Invokelogging text block to access. However, according to the MSDN Control.InvokeRequired documentation :
true if the control manuscript was created in a different thread than the calling thread (indicating that you should make calls using the invoke method); otherwise false.
Since this is a single-threaded scenario, how to InvokeRequiredbe true?
PS: I know that Task.Delayis a thing. I want to understand why the user interface thread is not blocked during Thread.Sleep.
Log output ::
[T9] Before await
[T9] [I] Task Start
[T9] [I] Task End
[T9] After await
The flashing button also shows the identifier of the thread stream in which the tick event handler is executed, as well as 9.
Full code :
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");
}
}
}