异步递归方法
本文关键字:递归方法 异步 | 更新日期: 2023-09-27 18:17:53
好吧...所以我正在转换我的一些简单使用的应用程序,以停止使用后台工作者系统将事情转换为标准异步。我使用异步从头开始构建了一个 WPF 应用程序,它运行得非常出色,所以我想转换其余部分以执行相同的操作(只是让我更容易阅读代码(。在这种情况下,我使用一种方法来清除目录,然后再从存储构建的机器(编译和存放它们的位置(复制文件和目录。我在使用"Empty"方法时遇到问题,我必须递归才能正常运行。这是目前的方法(有些事情是错误的(:
public static Task Empty(string targetDir)
{
return Task.Run(() =>
{
foreach (var directory in Directory.GetDirectories(targetDir))
{
Empty(directory);
string[] filelist2 = Directory.GetFiles(directory);
foreach (string files in filelist2)
{
File.SetAttributes(files, FileAttributes.Normal);
File.Delete(files);
}
if (!Directory.EnumerateFileSystemEntries(directory).Any())
{
Directory.Delete(directory, false);
}
}
string[] filelist = Directory.GetFiles(targetDir);
foreach (string files in filelist)
{
File.SetAttributes(files, FileAttributes.Normal);
File.Delete(files);
}
});
}
现在,它的作用是删除任何文件和子目录。它使用后台工作者工作(以前没有任务或任何东西(,但尝试在任务中运行最终会弹出一个关于无法找到文件的异常。我的猜测是这与线程有关,但我似乎无法弄清楚是什么。
任何想法可能导致问题?当它尝试对文件设置属性时它会失败(不是每次都同一个文件......似乎一旦它被递归循环多次,它就无法更改文件属性(。
等待递归调用
public static Task Empty(string targetDir)
{
return Task.Run(async () =>
{
foreach (var directory in Directory.GetDirectories(targetDir))
{
await Empty(directory);
string[] filelist2 = Directory.GetFiles(directory);
foreach (string files in filelist2)
{
File.SetAttributes(files, FileAttributes.Normal);
File.Delete(files);
}
if (!Directory.EnumerateFileSystemEntries(directory).Any())
{
Directory.Delete(directory, false);
}
}
string[] filelist = Directory.GetFiles(targetDir);
foreach (string files in filelist)
{
File.SetAttributes(files, FileAttributes.Normal);
File.Delete(files);
}
});
}
编辑这里也有很多需要改进的地方。您正在删除文件两次。要解决此问题,您可以减少代码,例如
public static Task<bool> Empty(string targetDir)
{
return Task.Run(async () =>
{
foreach (var directory in Directory.GetDirectories(targetDir))
{
if (await Empty(directory))
Directory.Delete(directory, false);
}
var retval = true;
foreach (string file in Directory.GetFiles(targetDir))
{
try
{
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
catch(Exception ex)
{
// something went wrong: log ex
retval = false;
}
}
return retval;
});
}
但这仍然不是真正的性能,因为您仍在等待递归调用返回。正如@Servy建议的那样,将创建许多无用的任务。让我向您展示一种仅通过一项任务完成此操作的方法。我们定义了一个同步函数:
public static bool Empty(string targetDir)
{
foreach (var directory in Directory.GetDirectories(targetDir))
{
if (Empty(directory))
Directory.Delete(directory, false);
}
var retval = true;
foreach (string file in Directory.GetFiles(targetDir))
{
try
{
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
catch(Exception ex)
{
// something went wrong: log ex
retval = false;
}
}
return retval;
}
现在我们定义它的异步版本:
public static Task<bool> EmptyAsync(string targetDir)
{
return Task.Run(() => this.Empty(targetDir));
}
这可能具有与每次回避调用创建任务相同/更好的性能。
看起来您要删除相同的文件两次,并且由于您在不同的线程中执行此操作,每个线程在开始删除之前枚举完整列表,因此您最终将尝试删除一个线程中的文件,而另一个线程中已经删除了该文件。
考虑文件:
/a/1
/a/2
现在考虑在文件夹/上运行代码。首先,您将递归到/a,这将(在单独的线程中(删除方法底部循环中的文件/a/1 和/a/2。同时,您将在方法顶部的循环中枚举文件/a/1 和/a/2。其中一个将在另一个之前发生,因此您将从其中一个或另一个获得FileNotFound。