【问题标题】:How not to block UI如何不阻止用户界面
【发布时间】:2021-04-01 01:38:38
【问题描述】:

我有一个应用程序,它可以加载 XML 文件,然后将其解析为一些不同的类。 它有自己的窗口,带有“加载文件”之类的按钮

有一些原理图代码是什么样子的

///In main window 

public void Button_Click ()
{
   //(VM)DataContext.AddNewFile();
}

///In VM

public void AddNewFile()
{
   //Some code with file dialogs and messages with several methods, one of which leads to AddMyType()
}

private void AddMyType(string file)
{
   //Creates class of myType
}

///In Type class constructor

internal MyType(string file)
{
   //Launches XML parser class to parse data from XML and create from this all MyType
   data = ParseFile(file).Result;
}

///In XML parser class:

internal async Task<MyTypeData>ParseFile(file)
{
   //Main method which launches background tasks and shows messages with progress bar 
   //Should update UI

   _progress = new Progress<int>(number =>
   {
       _message.UpdateProgress(number);
   });

   someData = await Task.Run(()=> BackgroundWorkTask(rawData, _progress));
   _message.Show(_maximumProgressValue); //Shows custom message with progress bar
   someOtherData = await Task.Run(()=> BackgroundWorkTask(otherRawData, _progress));
}

private <T> Task BackgroundWorkTask(rawData)
{
   //Some big work going here and gives some result
}

问题是 UI 冻结。

我尝试了很多变体:

Task.Wait(),等待BackgroundWorkTask并用

进行测试
private <T> Task BackgroundWorkTask()
{
   //Freezes
   while(true){} 
   Task.Delay(x)
   Task.Delay(x).Wait()
   Thread.Sleep(x)
   and await Task.Delay(x) (if private async Task)
}

我猜这是因为之前的所有主要工作都在主线程中进入同步模式。但是在启动 ParseFile() 方法之前,我确实对主线程中的数据进行了很多不同的操作。

据我了解,async 方法与 await Task.Run(()=>);用于在其他线程上做一些工作,不应阻塞 UI。

我认为使用异步方法来更新 UI 并等待不同的任务会有所帮助,但是......

如果有办法解决这个问题?

【问题讨论】:

  • 你写了ParseFile(file).Result;。它会阻止您的构造函数执行,直到您的 ParseFile 方法完成。
  • 准则:总是await 方法调用返回Task,因此只能从返回Task 本身的async 方法调用它们(除了可以是async void 的事件处理程序)
  • 关于文件解析代码的构造函数,可能违反OOP原则,请查看how much work should be done in a constructor?
  • 非常感谢!我会检查你所有的建议并分享结果。感谢有关构造函数的信息。

标签: c#


【解决方案1】:

使用Result 会阻塞您的UI 线程,并且由于您不能拥有async 构造函数,您将需要探索一种替代方法,其中有很多:

  1. 向构造函数传递数据而不是文件路径:

    private async Task AddMyType(string file)
    {
       var data = await ParseFile(file);
       var myType = new MyType(data);
    }
    
    ///In Type class constructor
    
    internal MyType(Data data)
    {
        this.data = data;
    }
    

  1. MyType 中使用async 工厂方法:

    class MyType
    {
        private MyType(Data data)
        {
            this.data = data;
        }
    
        internal static async Task<MyType> CreateAsync(string file)
        {
            var data = await ParseFile(file);
            return new MyType(data);
        }
    }
    

  1. 制作使用data async 的方法:

    class MyType
    {
        Task<Data> dataTask;
    
        internal MyType(string file)
        {
            dataTask = ParseFile(file);
        }
    
        public async Task MethodThatUsesData()
        {
            var data = await dataTask;
            // Use data..
        }
    }
    

【讨论】:

  • 非常感谢!这真的很有帮助!
猜你喜欢
  • 2018-03-17
  • 2013-01-07
  • 2015-10-04
  • 1970-01-01
  • 1970-01-01
  • 2016-10-31
  • 2017-01-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多