C# Async await-避免阻塞UI是正确的想法吗?

C# Async await-避免阻塞UI是正确的想法吗?,c#,asynchronous,C#,Asynchronous,我认为使用await/async是确保响应性UI的最佳方法。但我似乎做错了什么,因为我的应用程序无论如何都会冻结 我尝试做什么: 按钮已启动,应等待长时间操作 public async Task CmdLoadExcel() { // läd die excel datei string[] excelFiles = this.GetExcelFiles(); ExcelLoader eloader = new ExcelLoader(); await eloader.

我认为使用await/async是确保响应性UI的最佳方法。但我似乎做错了什么,因为我的应用程序无论如何都会冻结

我尝试做什么: 按钮已启动,应等待长时间操作

public async Task CmdLoadExcel()
{
    // läd die excel datei
   string[] excelFiles = this.GetExcelFiles();
   ExcelLoader eloader = new ExcelLoader();
   await eloader.StartLoading(excelFiles);
   foreach (DataSet elem in eloader.Tables)
   {
       this.ActivateItem(new ExcelViewModel(elem) { DisplayName = elem.DataSetName });
   }
}
推出的课程:

public async Task StartLoading(string[] files)
        {
            foreach (string file in files)
            {
                Stopwatch swatch = new Stopwatch();
                swatch.Start();
                System.Threading.Thread.Sleep(5000);
                FileInfo finfo = new FileInfo(file);
                using (ExcelPackage package = new ExcelPackage(finfo))
                {
                // very long operation
                }
            } // For Each
        } // StartLoading

它只是在睡眠时停止,我不确定我做错了什么。

用async和wait标记方法本身并不能做任何事情。您需要等待一项任务:

这将有助于:

public async Task StartLoading(string[] files)
{
    return Task.Run(() =>
    {            
        foreach (string file in files)
        {
            Stopwatch swatch = new Stopwatch();
            swatch.Start();
            System.Threading.Thread.Sleep(5000);
            FileInfo finfo = new FileInfo(file);
            using (ExcelPackage package = new ExcelPackage(finfo))
            {
            // very long operation
            }
        } // For Each
    };
} // StartLoading

用async和await标记方法本身并没有任何作用。您需要等待一项任务:

这将有助于:

public async Task StartLoading(string[] files)
{
    return Task.Run(() =>
    {            
        foreach (string file in files)
        {
            Stopwatch swatch = new Stopwatch();
            swatch.Start();
            System.Threading.Thread.Sleep(5000);
            FileInfo finfo = new FileInfo(file);
            using (ExcelPackage package = new ExcelPackage(finfo))
            {
            // very long operation
            }
        } // For Each
    };
} // StartLoading

问题是,令人吃惊的任务本身并不是异步的。您可以使用以下代码修复此问题:

public Task StartLoading(string[] files)
{
    return Task.Factory.StartNew(() =>
        {
            foreach (string file in files)
            {
                Stopwatch swatch = new Stopwatch();
                swatch.Start();
                System.Threading.Thread.Sleep(5000);
                FileInfo finfo = new FileInfo(file);
                using (ExcelPackage package = new ExcelPackage(finfo))
                {
                        // very long operation
                }
            } // For Each    
        });
} // StartLoading
然后您可以按如下方式使用代码:

public Task CmdLoadExcel()
{
    // läd die excel datei
   string[] excelFiles = this.GetExcelFiles();
   ExcelLoader eloader = new ExcelLoader();

   var task = eloader.StartLoading(excelFiles);
   return task.ContinueWith(t =>
            {
                foreach (DataSet elem in eloader.Tables)
                {
                    this.ActivateItem(new ExcelViewModel(elem) { DisplayName = elem.DataSetName });
                }
            });
 }

问题是,令人吃惊的任务本身并不是异步的。您可以使用以下代码修复此问题:

public Task StartLoading(string[] files)
{
    return Task.Factory.StartNew(() =>
        {
            foreach (string file in files)
            {
                Stopwatch swatch = new Stopwatch();
                swatch.Start();
                System.Threading.Thread.Sleep(5000);
                FileInfo finfo = new FileInfo(file);
                using (ExcelPackage package = new ExcelPackage(finfo))
                {
                        // very long operation
                }
            } // For Each    
        });
} // StartLoading
然后您可以按如下方式使用代码:

public Task CmdLoadExcel()
{
    // läd die excel datei
   string[] excelFiles = this.GetExcelFiles();
   ExcelLoader eloader = new ExcelLoader();

   var task = eloader.StartLoading(excelFiles);
   return task.ContinueWith(t =>
            {
                foreach (DataSet elem in eloader.Tables)
                {
                    this.ActivateItem(new ExcelViewModel(elem) { DisplayName = elem.DataSetName });
                }
            });
 }
你的惊人方法似乎缺少任何等待陈述。async关键字不会神奇地使方法能够异步运行。您可以等待Task.Delay作为Thread.Sleep的异步替代品。对于非常长的操作,如果是CPU受限的,可能需要生成一个新线程。如果它是IO绑定的,那么问题在于您是否有权访问可以使用的异步API

请参阅MSDN第9频道,以了解良好介绍的开始。

您的惊人加载方法似乎缺少任何等待声明。async关键字不会神奇地使方法能够异步运行。您可以等待Task.Delay作为Thread.Sleep的异步替代品。对于非常长的操作,如果是CPU受限的,可能需要生成一个新线程。如果它是IO绑定的,那么问题在于您是否有权访问可以使用的异步API


请参阅MSDN第9频道,了解一个好的介绍的开始。

我怀疑问题可能是等待eloader.StartingExcelFiles的行;实际上正在同一UI线程上运行。您需要像下面这样执行它。这将导致该方法在线程池线程上运行

await Task.Run(eloader.StartLoading(excelFiles));

我怀疑问题可能是该行正在等待eloader.startingExcelFiles;实际上正在同一UI线程上运行。您需要像下面这样执行它。这将导致该方法在线程池线程上运行

await Task.Run(eloader.StartLoading(excelFiles));

用wait Task.Delay5000代替它。我想,请仔细阅读一下异步,而不仅仅是实验。我有,包括,都很好,现在有几本书也涵盖了它。@Stephen Cleary:事实上,我之前读过你的博客文章-不幸的是,我没有抓住线程部分。附带说明:在尝试从您的博客文章中获取TAP文档时,出现了一个错误。也许链接已经过时了?我在一两个月前更改了链接。我想,请仔细阅读一下异步,而不仅仅是实验。我有,包括,都很好,现在有几本书也涵盖了它。@Stephen Cleary:事实上,我之前读过你的博客文章-不幸的是,我没有抓住线程部分。附带说明:在尝试从您的博客文章中获取TAP文档时,出现了一个错误。也许链接已经过时了?我在一两个月前更改了链接。只是仔细检查了一下,它现在正在工作…@Erik Schierboom你们两个方法都导致了一个错误,返回语句后不允许使用object语句。我使用的是德语版本,所以这是一个粗略的翻译。你在返回语句后做了什么吗?还是按原样实现了代码段?在很长的操作部分有几个过程调用,包括try..catch。epplus包在using块中使用。如果您只是按原样包含代码段,它不会给您错误。请尝试一下,如果错误消失了,那么请在很长的操作中发布代码-part@ChristianSauer很抱歉,我的代码有一个错误。我刚刚更新了我的答案。@Erik Schierboom你们两个方法在返回语句之后都导致了一个错误。不允许使用对象语句。我使用的是德语版本,所以这是一个粗略的翻译。你们在返回语句之后做什么了吗?还是按原样实现了代码段?在很长的操作部分有几个过程调用,包括try..catch。epplus包在using块中使用。如果您只是按原样包含代码段,它不会给您错误。请尝试一下,如果错误消失了,那么请在很长的操作中发布代码-part@ChristianSauer很抱歉,我的代码有一个错误。我刚刚更新了我的答案。谢谢你的解释和链接。事实上,这个程序可能是两者兼而有之:它读取数据并动态转换它们。谢谢你的解释和链接。行为
事实上,这个程序可能是两者兼而有之:它可以动态读取数据并进行转换。感谢您的帮助!我会用ContinueWith方法查找,我不知道它做什么@ChristianSauer ContinueWith方法的基本功能是允许将代码附加到方法完成时调用的任务。谢谢你的帮助!我会用ContinueWith方法查找,我不知道它做什么@ChristianSauer ContinueWith方法的基本功能是允许将代码附加到方法完成时调用的任务。我假定您不想在惊人的加载完成之前运行“foreach DataSet elem in eloader.Tables”语句。等待惊人加载将导致执行返回到最初调用CmdLoadExcel的方法,并仅在惊人加载完成后执行foreach。我猜想您不希望在惊人加载完成之前运行“foreach DataSet elem in eloader.Tables”语句。等待StartLoading将导致执行返回到最初调用CmdLoadExcel的方法,并仅在StartLoading完成后执行foreach。