与Task一起编辑UI的task.Wait()上的死锁 [英] DeadLock on task.Wait() with Task which edit UI
问题描述
我正在尝试为我的问题找到一些解决方案,但是没有结果(或者我只是没有正确地解决问题),因此,如果有人可以帮助/解释我,我将非常感激.
I'm trying to find some solutions to my problem here, but with no result (or I just do not get them right) so if anyone could help / explain i will be really gratefull.
我正在为使用Win Form的系统管理员开发工具,现在我需要在后台运行的所选计算机上创建一个连续的ping.UI上有一个在线状态指示器,我需要使用背景ping进行编辑.所以现在我处于这种状态:
I'm just developing a tool for system administrators using Win Form and now I need to create a continuous ping on the selected machine which is running on the background. There is an indicator for Online status on UI which I need to edit with background ping. So right now I'm in this state:
A类(获胜表格):
ClassB activeRelation = new ClassB();
public void UpdateOnline(Relation pingedRelation)
{
//There is many Relations at one time, but form shows Info only for one...
if (activeRelation == pingedRelation)
{
if (p_Online.InvokeRequired)
{
p_Online.Invoke(new Action(() =>
p_Online.BackgroundImage = (pingedRelation.Online) ? Properties.Resources.Success : Properties.Resources.Failure
));
}
else
{
p_Online.BackgroundImage = (pingedRelation.Online) ? Properties.Resources.Success : Properties.Resources.Failure;
}
}
}
//Button for tunring On/Off the background ping for current machine
private void Btn_PingOnOff_Click(object sender, EventArgs e)
{
Button btn = (sender is Button) ? sender as Button : null;
if (btn != null)
{
if (activeRelation.PingRunning)
{
activeRelation.StopPing();
btn.Image = Properties.Resources.Switch_Off;
}
else
{
activeRelation.StartPing(UpdateOnline);
btn.Image = Properties.Resources.Switch_On;
}
}
}
B类(代表与某台计算机的关系的类)
Class B (class thats represent relation to some machine)
private ClassC pinger;
public void StartPing(Action<Relation> action)
{
pinger = new ClassC(this);
pinger.PingStatusUpdate += action;
pinger.Start();
}
public void StopPing()
{
if (pinger != null)
{
pinger.Stop();
pinger = null;
}
}
C类(后台Ping类)
Class C (background ping class)
private bool running = false;
private ClassB classb;
private Task ping;
private CancellationTokenSource tokenSource;
public event Action<ClassB> PingStatusUpdate;
public ClassC(ClassB classB)
{
this.classB = classB;
}
public void Start()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
ping = PingAction(token);
running = true;
}
public void Stop()
{
if (running)
{
tokenSource.Cancel();
ping.Wait(); //And there is a problem -> DeadLock
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
private async Task PingAction(CancellationToken ct)
{
bool previousResult = RemoteTasks.Ping(classB.Name);
PingStatusUpdate?.Invoke(classB);
while (!ct.IsCancellationRequested)
{
await Task.Delay(pingInterval);
bool newResult = RemoteTasks.Ping(classB.Name);
if (newResult != previousResult)
{
previousResult = newResult;
PingStatusUpdate?.Invoke(classB);
}
}
}
所以当我取消令牌并等待Wait()完成任务时,问题就陷入了僵局->它仍在运行,但是Task中的While(...)正确完成了.
So the problem is in deadlock when I cancel token and Wait() for task to complete -> it's still running, but While(...) in task is finished right.
推荐答案
由于 ping.Wait();
阻止了UI线程,您陷入了僵局.
You have a deadlock because ping.Wait();
blocks UI thread.
您应该使用 await
异步地等待任务.
You should wait for task asynchronously using await
.
因此,如果 Stop()
是事件处理程序,则将其更改为:
So, if Stop()
is event handler then change it to:
public async void Stop() // async added here
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
如果不是:
public async Task Stop() // async added here, void changed to Task
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
如 @JohnB 所述,异步方法应具有 Async
后缀,因此,该方法应命名为 StopAsync()
.
As mentioned by @JohnB async methods should have Async
suffix so, the method should be named as StopAsync()
.
此处说明了类似的问题和解决方案-不阻止异步代码
Similar problem and solution are explained here - Do Not Block On Async Code
您应避免同步等待任务,因此应始终对任务使用 await
而不是 Wait()
或 Result
.另外,正如 @Fildor 所指出的那样,您应该一直使用 async-await
来避免这种情况.
You should avoid synchronous waiting on tasks, so you should always use await
with tasks instead of Wait()
or Result
. Also, as pointed by @Fildor you should use async-await
all the way to avoid such situations.
这篇关于与Task一起编辑UI的task.Wait()上的死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!