Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/264.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 异步递归方法_C#_Asynchronous_Recursion_Task Parallel Library - Fatal编程技术网

C# 异步递归方法

C# 异步递归方法,c#,asynchronous,recursion,task-parallel-library,C#,Asynchronous,Recursion,Task Parallel Library,好吧,我将我的一些简单应用程序转换为标准异步,以停止使用backgroundworker系统。我使用async从头开始构建了一个WPF应用程序,它工作得非常出色,所以我想将其余的转换为同样的方式(只是让我更容易阅读代码)。在本例中,我使用一种方法来清除目录,然后从存储构建的机器(编译和存放构建的地方)复制文件和目录。我对“Empty”方法有一个问题,我必须递归地使它正常工作。以下是目前的方法(有些事情是错误的): 现在要做的是删除所有文件和子目录。它使用backgroundworker工作(以前

好吧,我将我的一些简单应用程序转换为标准异步,以停止使用backgroundworker系统。我使用async从头开始构建了一个WPF应用程序,它工作得非常出色,所以我想将其余的转换为同样的方式(只是让我更容易阅读代码)。在本例中,我使用一种方法来清除目录,然后从存储构建的机器(编译和存放构建的地方)复制文件和目录。我对“Empty”方法有一个问题,我必须递归地使它正常工作。以下是目前的方法(有些事情是错误的):

现在要做的是删除所有文件和子目录。它使用backgroundworker工作(以前没有任务或任何任务),但尝试运行任务最终会弹出一个异常,即无法找到文件。我猜这和线程有关,但我似乎不知道是什么


你知道是什么导致了这个问题吗?当它尝试在文件上设置属性时失败(不是每次都是同一个文件…只是似乎它递归循环多次后,就无法更改文件属性)。

看起来您正在删除相同的文件两次,由于您在不同的线程中执行此操作,每个线程在开始删除之前都会枚举完整列表,因此最终您将尝试删除一个线程中已在另一个线程中删除的文件

考虑以下文件:

/a/1

/a/2

现在考虑在文件夹上运行代码。


首先,您将递归到/a,它将(在单独的线程中)删除方法底部循环中的文件/a/1和/a/2。同时,您将在方法顶部的循环中枚举文件/a/1和/a/2。其中一个将在另一个之前发生,因此您将从其中一个或另一个获取FileNotFound。

等待递归调用

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;
    });
}
现在我们定义了它的异步版本:

public static Task<bool> EmptyAsync(string targetDir)
{
    return Task.Run(() => this.Empty(targetDir));
}
公共静态任务EmptyAsync(字符串targetDir)
{
返回Task.Run(()=>this.Empty(targetDir));
}

这可能与每次递归调用创建任务具有相同/更好的性能。

值得一提的是,这不是一种异步方法。这只是一个使用TPL的方法。@esskar它在那里
空(目录)Marcel:因为我将启动它的按钮设置为async,并且在实际的方法调用本身上使用waits,所以我认为它仍然使用asynchronous,但我猜我使用的是TPL。所以我递归调用Empty的原因是,这是我似乎可以找到所有子目录中的所有文件并在删除文件夹之前删除它们的唯一方法。我尝试的任何其他方法都没有删除所有内容。好的,似乎就是这样。当我查看它时,该文件已被删除。所以我认为它这样做的原因是因为backgroundworker是同步进行的,而我尝试使用async进行一些工作,这对吗?因此,它同时生成两个文件列表,而不是在每个循环中分别创建列表。我认为您混淆了异步方法和并行处理这两个独立但相关的主题。这里所做的是并行处理,而不是异步方法调用。异步调用通过在被调用方法完成时发出通知来区分,以便调用线程可以检索结果。就是这样!你和布赖恩在这方面帮了大忙。我一读到你的代码就明白我做错了什么。当我调试时,我意识到它不是在等待递归,但我不知道如何让它正常工作(添加wait不起作用,但这是因为我没有在Task.Run中指定async)。非常感谢大家:)这将只是去序列化一切,完全违背了旋转一大堆任务的目的。您也可以删除所有异步代码,然后返回同步算法。
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));
}