1

在 MVC 4 应用程序中,我有一个将文件复制到目标文件夹的任务。因为我有多个文件,所以我为每个文件创建一个任务,我想等到它们都完成。问题是我的代码块Task.WaitAll(copyingTasks.ToArray())好像任务永远不会结束,所以它永远不会通过那行代码。下面是示例代码:

    private void CopyFilesFromWorkingCopyForProject(string projectName)
    {
        var copyingTasks = new List<Task>
            {
                CopyAllFromDirectoryToDirectory(FilesUtils.AndroidConfigsPath(), FilesUtils.AndroidPathForProject(projectName)),
                CopyAllFromDirectoryToDirectory(FilesUtils.AndroidValuesPath(), FilesUtils.AndroidPathForProject(projectName)),
                CopyFileToDirectory(FilesUtils.AndroidManifestPath(), FilesUtils.AndroidPathForProject(projectName)),
                CopyAllFromDirectoryToDirectory(FilesUtils.IosConfigsPath(), FilesUtils.IosPathForProject(projectName))

            };
        Task.WaitAll(copyingTasks.ToArray());
    }

    private async Task CopyAllFromDirectoryToDirectory(string sourceDirectory, string destinationDirectory)
    {
        foreach (string filename in Directory.EnumerateFiles(sourceDirectory))
        {
            await CopyFileToDirectory(filename, destinationDirectory);
        }
    }

    private async Task CopyFileToDirectory(string filename, string destinationDirectory)
    {
        using (FileStream sourceStream = File.Open(filename, FileMode.Open))
        {
            using (FileStream destinationStream = File.Create(destinationDirectory + filename.Substring(filename.LastIndexOf('\\'))))
            {
                await sourceStream.CopyToAsync(destinationStream);
            }
        }
    }

如果我评论 Task.WaitAll(copyingTasks.ToArray());它不再阻塞,但我想等待所有文件被复制。

4

1 回答 1

11

Combining await and synchronous wait leads to deadlocks, because async methods try to resume on the context that's currently blocked by your wait.

What you should do instead is to make CopyFilesFromWorkingCopyForProject() also async (and the method that calls that, and the method that calls that, …):

private async Task CopyFilesFromWorkingCopyForProject(string projectName)
{
    var copyingTasks = new List<Task>
        {
            CopyAllFromDirectoryToDirectory(FilesUtils.AndroidConfigsPath(), FilesUtils.AndroidPathForProject(projectName)),
            CopyAllFromDirectoryToDirectory(FilesUtils.AndroidValuesPath(), FilesUtils.AndroidPathForProject(projectName)),
            CopyFileToDirectory(FilesUtils.AndroidManifestPath(), FilesUtils.AndroidPathForProject(projectName)),
            CopyAllFromDirectoryToDirectory(FilesUtils.IosConfigsPath(), FilesUtils.IosPathForProject(projectName))

        };
    await Task.WhenAll(copyingTasks);
}

If you can't or don't want to do that, you need to make sure the async methods don't resume on the current context. To do that, you can use ConfigureAwait(false) for all your awaits, or you can call the async methods on a background thread using Task.Run().

于 2013-07-23T10:26:33.507 回答