好吧......所以我正在转换我的一些简单用途应用程序,以停止使用后台工作系统处理标准异步。我使用async从头开始构建了一个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);
}
});
}
现在这样做是删除任何文件和子目录。它使用backgroundworker(之前没有任何任务或任何内容),但尝试在任务中运行最终会弹出一个关于无法找到文件的异常。我的猜测是它与线程有关,但我似乎无法弄清楚是什么。
任何可能导致问题的想法?当它尝试在一个文件上设置SetAttributes时失败(每次都不是同一个文件......只是一旦它递归循环很多次,它无法改变文件属性)。
答案 0 :(得分:3)
看起来你正在删除相同的文件两次,并且因为你在不同的线程中进行它,每个线程在开始删除之前枚举完整列表,你最终会尝试删除一个已经存在的线程中的文件在另一个中被删除。
考虑文件:
/一个/ 1
/ A / 2
现在考虑在文件夹/上运行你的代码。 首先,你将进入/ a,它将(在一个单独的线程中)删除方法底部循环中的文件/ a / 1和/ a / 2。同时,您将枚举方法顶部循环中的文件/ a / 1和/ a / 2。其中一个将在另一个之前发生,因此您将从其中一个或另一个获得FileNotFound。
答案 1 :(得分:2)
等待递归调用
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));
}
这可能与每次recusrsion调用创建Task相同/更好的性能。