IamaC IamaC - 2 months ago 91
C# Question

async/await with thread timer

I was able resolve the issue with the input form Stephen Cleary. Please see update 3.

I have a Windows Forms application that has a method with the async modifier. If the method is called inside a button's click event it does not block the UI thread. However, when I call it inside a timer as a callback it freezes the UI. I could not figure out what I am doing wrong here. Please see below my code. This is just a sample project for demonstration purposes.

public Form1()
{
InitializeComponent();
}

private async void formCloseAsync()
{
shutdown stf = new shutdown();
stf.StartPosition = FormStartPosition.CenterScreen;
stf.Show();
var task = Task.Factory.StartNew(processClose);
await task;
}

private void processClose()
{
Thread.Sleep(5000);
Environment.Exit(1);
}

private void simpleButtonAsync_Click(object sender, EventArgs e)
{
formCloseAsync();
}

private void _simpleButtonTimer_Click(object sender, EventArgs e)
{
Timer _shutdownTimer = new Timer(delegate
{
formCloseAsync();
}, null, 5000, Timeout.Infinite);
}


Update 1:
Thanks All for your valuable input. Please see the updated code below

public Form1()
{
InitializeComponent();

}

private Timer _shutdownTimer;
private void formCloseAsync()
{
shutdown stf = new shutdown();
stf.StartPosition = FormStartPosition.CenterScreen;
stf.Show();

Task.Run(async ()=>
{
await Task.Delay(5000);
Environment.Exit(1);
}
);
}

private void simpleButtonAsync_Click(object sender, EventArgs e)
{
formCloseAsync();
}

private void _simpleButtonTimer_Click(object sender, EventArgs e)
{
_shutdownTimer = new Timer(async delegate
{
formCloseAsync();
}, null, 0, Timeout.Infinite);
}


However I still have the same issue. shutdown stf is blocked when called inside timer callback. Main UI is okay. No issue with that. I just want the shutdown(stf) GUI to be responsive when created from timer call back. Shutdown GUI has a progress bar.

Update 3:

Here is the final code

public partial class Form1 : Form
{
readonly shutdown _stf = new shutdown();
public Form1()
{
InitializeComponent();

}

private Timer _shutdownTimer;
private async Task formCloseAsync()
{

try
{
BeginInvoke(new MethodInvoker(delegate
{

_stf.StartPosition = FormStartPosition.CenterScreen;
_stf.Show();
}));
await Task.Run( async () =>
{
await Task.Delay(5000);
});

}
catch (Exception ex)
{

MessageBox.Show(ex.ToString());

}
finally
{
Environment.Exit(1);
}

}

private void simpleButtonAsync_Click(object sender, EventArgs e)
{
formCloseAsync();
}

private void _simpleButtonTimer_Click(object sender, EventArgs e)
{
_shutdownTimer = new Timer(async delegate
{
formCloseAsync();
}, null, 0, Timeout.Infinite);
}

private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
await formCloseAsync();
}

Answer

However, when I call it inside a timer as a callback it freezes the UI.

You can't access UI elements from background threads (I'm guessing stf.Show(); is showing a dialog). The easiest fix is probably to replace System.Threading.Timer with Windows.Forms.Timer.

Other notes: