C# 异步等待导致UI被阻止
我有一个问题,我有一个普通函数,它调用一个异步函数,它调用循环中的另一个异步函数。问题是,有对我的硬件接口的调用(比如执行电机运动的扫描),出于某种原因,这似乎会持续阻塞UI线程,直到我们实际完成整个“启动”功能 我不确定我做错了什么,但我觉得某些部分不应该在UI线程上完成,而应该在另一个线程上完成。。。我不完全确定怎么做。下面是一个代码示例。每次扫描时,UI都会更新,每次扫描时突出显示“按钮”C# 异步等待导致UI被阻止,c#,wpf,multithreading,async-await,C#,Wpf,Multithreading,Async Await,我有一个问题,我有一个普通函数,它调用一个异步函数,它调用循环中的另一个异步函数。问题是,有对我的硬件接口的调用(比如执行电机运动的扫描),出于某种原因,这似乎会持续阻塞UI线程,直到我们实际完成整个“启动”功能 我不确定我做错了什么,但我觉得某些部分不应该在UI线程上完成,而应该在另一个线程上完成。。。我不完全确定怎么做。下面是一个代码示例。每次扫描时,UI都会更新,每次扫描时突出显示“按钮” public void StartProcess(ObservableCollection&
public void StartProcess(ObservableCollection<ObjModel> objs)
{
// Cancel
if (cancellationTokenSource != null)
{
// Cancel tasks
cancellationTokenSource.Cancel();
cancellationTokenSource.Dispose();
return;
}
cancellationTokenSource = new CancellationTokenSource();
if (MachineController.Instance.InitHardware())
{
StartScan(objs);
}
else
{
Clean();
}
}
private async void StartScan(ObservableCollection<ObjModel> objs)
{
// We are now running
InfoModel.IsRunning = true;
for (int index = 1; index < Size; index++)
{
MachineController.Instance.MoveToIndex(index - 1);
if ((index - 1) % 2 == 0)
{
for (int iAIndex = 1; iAIndex < Size; iAIndex++)
{
var obj = objs.Where(x => x.R == index && x.C == iAIndex).FirstOrDefault();
await DoScan(obj);
}
}
}
Clean();
}
private async Task DoScan(ObjModel obj)
{
MachineController.Instance.MoveToCIndex(obj.C - 1);
// Set the task to only take a couple of seconds
bool check = MachineController.Instance.Scan();
if (check)
{
await Task.Delay(5, cancellationTokenSource.Token);
// Plot
UpdateGraph(obj.R, obj.C);
}
}
public void启动过程(obbservableCollection objs)
{
//取消
if(cancellationTokenSource!=null)
{
//取消任务
cancellationTokenSource.Cancel();
cancellationTokenSource.Dispose();
返回;
}
cancellationTokenSource=新的cancellationTokenSource();
if(MachineController.Instance.InitHardware())
{
StartScan(objs);
}
其他的
{
清洁();
}
}
专用异步void StartScan(ObservableCollection objs)
{
//我们现在正在跑步
InfoModel.IsRunning=true;
对于(int index=1;indexx.R==index&&x.C==iAIndex).FirstOrDefault();
等待多斯坎(obj);
}
}
}
清洁();
}
专用异步任务DoScan(ObjModel obj)
{
MachineController.Instance.MoveToCIndex(obj.C-1);
//将任务设置为只需几秒钟
bool check=MachineController.Instance.Scan();
如果(检查)
{
等待任务延迟(5,cancellationTokenSource.Token);
//密谋
更新图(obj.R、obj.C);
}
}
显然在UI线程中调用了MachineController.Instance
方法,可能会阻止它
要避免阻塞UI线程,请在任务中执行以下方法:
private async Task DoScan(ObjModel obj)
{
bool check = await Task.Run(() =>
{
MachineController.Instance.MoveToCIndex(obj.C - 1);
return MachineController.Instance.Scan();
}
...
}
第一种选择是,你实际上并没有做多任务处理 然而,还有一种选择:你正在用更新淹没GUI线程,使它看起来像GUI线程被阻塞了一样(当它真的、真的被占用的时候) 编写GUI是一项昂贵的操作。如果每个用户触发的事件只执行一次,您将永远不会注意到。但是从一个循环开始——特别是在多任务中运行的循环——你会很快注意到它。在我进入多线程的第一步中,我实际上遇到了这个精确的问题。我太频繁地更新GUI,将其锁定 我确实编写了一些示例代码来展示它:
using System;
using System.Windows.Forms;
namespace UIWriteOverhead
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
int[] getNumbers(int upperLimit)
{
int[] ReturnValue = new int[upperLimit];
for (int i = 0; i < ReturnValue.Length; i++)
ReturnValue[i] = i;
return ReturnValue;
}
void printWithBuffer(int[] Values)
{
textBox1.Text = "";
string buffer = "";
foreach (int Number in Values)
buffer += Number.ToString() + Environment.NewLine;
textBox1.Text = buffer;
}
void printDirectly(int[] Values){
textBox1.Text = "";
foreach (int Number in Values)
textBox1.Text += Number.ToString() + Environment.NewLine;
}
private void btnPrintBuffer_Click(object sender, EventArgs e)
{
MessageBox.Show("Generating Numbers");
int[] temp = getNumbers(10000);
MessageBox.Show("Printing with buffer");
printWithBuffer(temp);
MessageBox.Show("Printing done");
}
private void btnPrintDirect_Click(object sender, EventArgs e)
{
MessageBox.Show("Generating Numbers");
int[] temp = getNumbers(1000);
MessageBox.Show("Printing directly");
printDirectly(temp);
MessageBox.Show("Printing done");
}
}
}
使用系统;
使用System.Windows.Forms;
命名空间UIWriteOverhead
{
公共部分类Form1:Form
{
公共表格1()
{
初始化组件();
}
int[]getNumbers(int上限)
{
int[]ReturnValue=新int[上限];
for(int i=0;i
不在Text属性上执行字符串浓缩可以在数量级上提高性能。在这种情况下,我不确定绘图行为有多智能:它会为每个+=,还是只为第一个,将重画排队。但在多任务处理中,您通常每次都会得到一个平局。除了事件处理程序外,不要使用
async void
方法。除此之外,MachineController.Instance.Scan()
显然在UI线程中被调用并阻止它。您可以将其包装为wait Task.Run(()=>MachineController.Instance.Scan())代码>你好,克莱门斯。谢谢那么,我应该从StartScan函数中删除async吗?DoScan仍然可以拥有wait吗?…不,它应该声明为私有异步任务StartScan(…)
,并在调用时等待。就像DoScan
。这同样适用于StartProcess
StartScan(objs)代码>->等待StartScan(objs)代码>唯一不会在调用线程上运行的代码将是Task.Delay()
(实际上,我相信它将使用计时器)。您等待的事实是这是您的代码异步但未达到预期水平的唯一原因。其余的运行在调用线程上,当然是UI线程,因此阻塞。您忘记使用异步I/O了吗Task.Run()
?答案就是全部