为啥 Stream.CopyTo 不直接写入文件?

Posted

技术标签:

【中文标题】为啥 Stream.CopyTo 不直接写入文件?【英文标题】:Why is Stream.CopyTo not writing directly to a file?为什么 Stream.CopyTo 不直接写入文件? 【发布时间】:2020-11-04 12:20:13 【问题描述】:

此操作块连接到带有签名var getStream = new TransformBlock<FileChunk, Tuple<Task<HttpResponseMessage>, FileChunk>> 的转换块,但是流并未直接写入提供的文件。出于某种原因,我认为它仍然被缓存到内存中。任务是元组初始化为client.SendAsync(request, HttpCompletionOption.ResponseContentRead, CancellationToken.None);

var writeStream = new ActionBlock<Tuple<Task<HttpResponseMessage>, FileChunk>>(async task =>

    using (var streamToRead = await task.Item1.Result.Content.ReadAsStreamAsync())
    
        using (var fileToWriteTo = File.Open(task.Item2._tempfilename,
            FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
        
            await task.Item1.Result.Content.CopyToAsync(fileToWriteTo).ContinueWith(task1 =>
            
                var s = new FileChunk();
                Interlocked.Add(ref TasksDone, 1);
                asyncTasks.TryDequeue(out s);
            , CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion,
                TaskScheduler.Current);
        
    
, new ExecutionDataflowBlockOptions

    BoundedCapacity = Environment.ProcessorCount, // Cap the item count
    MaxDegreeOfParallelism = Environment.ProcessorCount, // Parallelize on all cores
);

关于如何解决此问题的任何建议?另外,我假设它首先缓存到内存,因为文件大小不会在刷新时更新或增加,而是一次全部更新。

【问题讨论】:

任务继续是怎么回事?尤其是 out s - 这不会去任何地方。 你有一个名为ConcurrentQueue&lt;FileChunk&gt;asyncTasks 也有点奇怪。 我很想看到minimal reproducible example 你在做什么? @Enigmativity 它不应该去任何地方。我正在使用自定义 ConcurrentQueue 来调用 Enqueue 和 TryDequeue 上的事件。您可以在我的 github repo 上准确了解我的用途 理想情况下,您应该在问题中发布足够的详细信息,以便能够看到发生了什么。当我快速查看您在 repo 中的代码时,我看到的一件事是贯穿始终使用 try catch (Exception ex) .. 代码。这只是吞下错误的好方法。谁知道这是否是这个问题的一部分。您应该将其全部删除,只捕获您可以有意义地处理的特定异常。 【参考方案1】:

我建议分离人工ContinueWith 延续,await 直接CopyToAsync 操作。使用您当前的设置,如果出现问题,您将不会获得任何信息,因为如果出现异常,TaskContinuationOptions.OnlyOnRanToCompletion 将导致继续被取消,并且 TPL 数据流块忽略 OperationCanceledExceptions。

这是我的建议:

var writeStream = new ActionBlock<Tuple<Task<HttpResponseMessage>, FileChunk>>(async task =>

    using (var streamToRead = await task.Item1.Result.Content.ReadAsStreamAsync())
    
        using (var fileToWriteTo = File.Open(task.Item2._tempfilename,
            FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
        
            await task.Item1.Result.Content.CopyToAsync(fileToWriteTo);
        
        Interlocked.Add(ref TasksDone, 1);
        asyncTasks.TryDequeue(out var s);
    
);

我还建议考虑将Tuple 替换为ValueTuple,以获得simplified syntax 并防止冗余分配:

var writeStream = new ActionBlock<(Task<HttpResponseMessage>, FileChunk)>(async task =>

最后我应该指出,我对尝试同时在同一个物理磁盘中写入多个文件的过程的性能特征持怀疑态度。

【讨论】:

以上是关于为啥 Stream.CopyTo 不直接写入文件?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的图像无法写入文件并显示?

为啥将原始字节写入分区不起作用[扇区写入]?

为啥 save() 第一次不写入文件?

写入二进制文件并用各种程序打开后,为啥结果不符合预期?

为啥我的 Linux 版本的 logstash 不通过换行写入输出文件

java中输出流不关闭为啥不能写入