Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/282.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 异步等待导致UI被阻止_C#_Wpf_Multithreading_Async Await - Fatal编程技术网

C# 异步等待导致UI被阻止

C# 异步等待导致UI被阻止,c#,wpf,multithreading,async-await,C#,Wpf,Multithreading,Async Await,我有一个问题,我有一个普通函数,它调用一个异步函数,它调用循环中的另一个异步函数。问题是,有对我的硬件接口的调用(比如执行电机运动的扫描),出于某种原因,这似乎会持续阻塞UI线程,直到我们实际完成整个“启动”功能 我不确定我做错了什么,但我觉得某些部分不应该在UI线程上完成,而应该在另一个线程上完成。。。我不完全确定怎么做。下面是一个代码示例。每次扫描时,UI都会更新,每次扫描时突出显示“按钮” public void StartProcess(ObservableCollection&

我有一个问题,我有一个普通函数,它调用一个异步函数,它调用循环中的另一个异步函数。问题是,有对我的硬件接口的调用(比如执行电机运动的扫描),出于某种原因,这似乎会持续阻塞UI线程,直到我们实际完成整个“启动”功能

我不确定我做错了什么,但我觉得某些部分不应该在UI线程上完成,而应该在另一个线程上完成。。。我不完全确定怎么做。下面是一个代码示例。每次扫描时,UI都会更新,每次扫描时突出显示“按钮”

    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()
?答案就是全部