c# - 如何防止在 IO 操作期间由于属性快速变化而阻塞 UI?
问题描述
我正在开发一个有时会删除文件夹的应用程序。为了向用户显示进度,我ProgressBar
在视图中使用了一个。因此,我的 ViewModeldouble SetupProgress
和double SetupProgressMax
. 我编译了要删除的文件夹中包含的所有文件的列表,每次成功删除文件后,我都会更新属性SetupProgress
。我不知道事先要删除的文件数量。
我的代码(浓缩为重要部分):
public class ViewModel : INotifyPropertyChanged
{
public double SetupProgress
{
get; set; // Notifies about changes
}
public double SetupProgressMax
{
get; set; // Notifies about changes
}
public async Task<bool> DeleteFiles(IList<string> filesToBeDeleted)
{
bool success = true;
SetupProgressMax = filesToBeDeleted.Count;
foreach (string filePath in filesToBeDeleted)
{
success = success && await IOHelper.TryDeleteFile(filePath);
if (success)
{
// Report that one item has been processed.
_OnProgressChanged();
}
else
{
break;
}
}
return success;
}
public void _OnProgressChanged()
{
// SetupProgress is the VM property bound by the ProgressBar
SetupProgress++;
}
}
public static class IOHelper
{
public static async Task<bool> TryDeleteFile(string filePath, int tries = 3)
{
while (tries > 0)
{
try
{
FileInfo fi = new FileInfo(filePath);
if (fi.IsReadOnly)
{
fi.IsReadOnly = false;
}
fi.Delete();
return true;
}
catch (FileNotFoundException)
{
return true;
}
catch (Exception ex)
{
tries--;
if (tries == 0)
{
// Log error
}
else
{
// Log warning
}
await Task.Delay(50);
}
}
return false;
}
}
我的问题是,在删除文件时,UI 线程完全阻塞,只有在操作完成后才会更新(所有文件都已删除)。
问题
- UI 进程阻塞的原因可能是什么?
- 我该如何规避这个?
更新:删除了我在发布问题之前测试过的解决方案,因为它们不起作用,甚至无法解决问题的根源。
解决方案
似乎TryDeleteFile
正在 UI 线程中执行。鉴于其当前的实现,它不是一个异步方法,不应该返回 aTask
而是 a bool
:
public static bool TryDeleteFile(string filePath)
{
try
{
FileInfo fi = new FileInfo(filePath);
if (fi.IsReadOnly)
{
fi.IsReadOnly = false;
}
fi.Delete();
return true;
}
catch (FileNotFoundException)
{
return true;
}
catch (Exception ex)
{
// Log Exception
return false;
}
}
该await
关键字是完全没有必要的,因为该方法缺少任何await
操作。
您可以使用以下方法在视图模型中的后台线程上调用同步方法Task.Run
:
public async Task<bool> DeleteFiles(IList<string> filesToBeDeleted)
{
...
foreach (string filePath in filesToBeDeleted)
{
success = success && await Task.Run(() => IOHelper.TryDeleteFile(filePath));
...
}
return success;
}
请注意,使用异步 API 公开真正同步的方法被认为是一种不好的做法。有关更多信息,请参阅Stephen Toub 的博客文章。
推荐阅读
- java - 根据得分找到赢家、输家和平局玩家
- amp-html - 是否可以在 AMP-HTML 中循环播放 vimeo 视频?
- python-3.x - Gekko优化问题中IMODE的选择
- javascript - 使用 React-Redux “connect” 时 App 类中的错误
- php - 什么填充了 Sylius 资源元数据注册表?
- javascript - material-ui:从 onClick() 处理程序中的芯片组件获取标签
- python-3.x - 在标准输入上使用相对导入的 Python 3 脚本给出错误:没有名为 '__main__.XXX' 的模块;'__main__' 不是一个包
- c++ - 无法打印数组或分配给它
- python - 在DecisionTree中使用StringIO的目的是什么
- bash - 试图理解 bash 函数的返回值