我在WinForm应用程序中启动了一些异步无限循环,但每次我试图突破它们时,程序都会挂起。我已经阅读了一些人们建议使用CancellationTokens的类似主题,但我无法根据自己的需要调整它们。这是我的代码的相关部分。
static bool processStop = false;
static bool processStopped = false;
//Called once
private async void ProcessData()
{
while (!processStop)
{
await Task.Run
(
() =>
{
//Do stuff and call regular not async methods
}
);
}
processStopped = true;
}
//Button click handler to exit WinForm
btnExit.Click += (senders, args) =>
{
processStop = true;
//Programm hangs up here
while (!processStopped);
FormMain.Close();
}
编辑代码
变量是静态的
Close方法是Forms的默认Close()方法。
答案 0 :(得分:3)
问题是对Task.Run
的调用在主线程上继续。 processStop = true;
和while (!processStopped);
一个接一个地同步执行。这不会让ProcessData
方法继续执行并发生死锁
我看到了几个解决方案:
将ConfigureAwait(false)
与Task.Run
:
private async void ProcessData()
{
while (!processStop)
{
await Task.Run
(
() =>
{
//Do stuff and call regular not async methods
}
).ConfigureAwait(false);
}
processStopped = true;
}
这将导致ProcessData
继续在线程池上,并且您已经通过调用Task.Run
使用了线程池,因此它不是一个很好的解决方案
将整个过程包裹在Task.Run
:
static volatile bool processStop = false;
static volatile bool processStopped = false;
//Called once
private async void ProcessData()
{
await Task.Run(() =>
{
while (!processStop)
{
...
}
processStopped = true;
});
}
这需要更改传递给它的方法的形式。
使ProcessData
成为一种同步方法来处理CPU密集型任务并正确调用它。 CancellationToken
将是取消任务的首选方式:
private void ProcessData(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
// do work
}
}
用这个称呼它:
Task processingTask;
CancellationTokenSource cts;
void StartProcessing()
{
cts = new CancellationTokenSource();
processingTask = Task.Run(() => ProcessData(cts.Token), cts.Token);
}
btnExit.Click += async (senders, args) =>
{
cts.Cancel();
try
{
await processingTask;
}
finally
{
FormMain.Close();
}
}
答案 1 :(得分:0)
如果你想在不阻塞的情况下旋转一堆任务,你可以这样做:
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//Called once
private async Task ProcessData()
{
int count = 0;
while (true)
{
await Task.Run
(
() =>
{
this.Invoke(new Action(() => {
label2.Text = (count++).ToString();
label1.Text = DateTime.Now.ToString(); }));
Thread.Sleep(100);
}
);
}
Debugger.Break(); //you will never see this hit at all
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
private async void button2_Click(object sender, EventArgs e)
{
await ProcessData();
}
}
}