为什么这段代码导致死锁?

时间:2016-09-30 12:32:17

标签: c# asynchronous async-await task-parallel-library

以下代码用于异步复制文件,但它会导致我的应用程序出现死锁。它使用名为'Interleaved(..)'found here的任务组合器辅助方法按照它们完成的顺序返回任务。

public static async Task<List<StorageFile>> CopyFiles_CAUSES_DEADLOCK(IEnumerable<StorageFile> sourceFiles, IProgress<int> progress, StorageFolder destinationFolder)
{
    List<StorageFile> copiedFiles = new List<StorageFile>();
    List<Task<StorageFile>> copyTasks = new List<Task<StorageFile>>();

    foreach (var file in sourceFiles)
    {
        // Create the copy tasks and add to list
        var copyTask = file.CopyAsync(destinationFolder, Guid.NewGuid().ToString()).AsTask();
        copyTasks.Add(copyTask);
    }

    // Serve up each task as it completes
    foreach (var bucket in Interleaved(copyTasks))
    {
        var copyTask = await bucket;
        var copiedFile = await copyTask;
        copiedFiles.Add(copiedFile);
        progress.Report((int)((double)copiedFiles.Count / sourceFiles.Count() * 100.0));
    }

    return copiedFiles;
}

我最初创建了一个更简单的'CopyFiles(...)',它按照提供的顺序处理任务(而不是完成),这很好用,但我无法弄清楚为什么这个经常死锁。特别是,当要处理的文件很多时。

以下是更简单的'CopyFiles'代码:

public static async Task<List<StorageFile>> CopyFiles_RUNS_OK(IEnumerable<StorageFile> sourceFiles, IProgress<int> progress, StorageFolder destinationFolder)
{
    List<StorageFile> copiedFiles = new List<StorageFile>();
    int sourceFilesCount = sourceFiles.Count();

    List<Task<StorageFile>> tasks = new List<Task<StorageFile>>();
    foreach (var file in sourceFiles)
    {
        // Create the copy tasks and add to list
        var copiedFile = await file.CopyAsync(destinationFolder, Guid.NewGuid().ToString()).AsTask();
        copiedFiles.Add(copiedFile);
        progress.Report((int)((double)copiedFiles.Count / sourceFilesCount *100.0));
    }

    return copiedFiles;
}

编辑:

为了找出发生了什么,我已经改变了CopyFiles(...)的实现以使用TPL Dataflow。我知道这段代码将按照它们提供的顺序返回项目,这不是我想要的,但它将Interleaved依赖项作为开始删除。无论如何,尽管如此,该应用程序仍然挂起。好像它没有从file.CopyAsync(..)调用返回。我当然有可能在这里做错事。

public static async Task<List<StorageFile>> CopyFiles_CAUSES_HANGING_ALSO(IEnumerable<StorageFile> sourceFiles, IProgress<int> progress, StorageFolder destinationFolder)
{
    int sourceFilesCount = sourceFiles.Count();
    List<StorageFile> copiedFiles = new List<StorageFile>();

    // Store for input files.
    BufferBlock<StorageFile> inputFiles = new BufferBlock<StorageFile>();
    // 
    Func<StorageFile, Task<StorageFile>> copyFunc = sf => sf.CopyAsync(destinationFolder, Guid.NewGuid().ToString()).AsTask();
    TransformBlock<StorageFile, Task<StorageFile>> copyFilesBlock = new TransformBlock<StorageFile, Task<StorageFile>>(copyFunc);

    inputFiles.LinkTo(copyFilesBlock, new DataflowLinkOptions() { PropagateCompletion = true });

    foreach (var file in sourceFiles)
    {
        inputFiles.Post(file);
    }
    inputFiles.Complete();

    while (await copyFilesBlock.OutputAvailableAsync())
    {
        Task<StorageFile> file = await copyFilesBlock.ReceiveAsync();
        copiedFiles.Add(await file);
        progress.Report((int)((double)copiedFiles.Count / sourceFilesCount * 100.0));
    }
    copyFilesBlock.Completion.Wait();

    return copiedFiles;
}

非常感谢您的任何帮助。

0 个答案:

没有答案