gpt4 book ai didi

c# - 写入具有多个流的文件 C#

转载 作者:太空狗 更新时间:2023-10-29 23:06:33 25 4
gpt4 key购买 nike

我正在尝试使用 HTTP 将一个大文件 (>1GB) 从一台服务器下载到另一台服务器。为此,我并行发出 HTTP 范围请求。这让我可以并行下载文件。

当保存到磁盘时,我获取每个响应流,打开同一个文件作为文件流,寻找我想要的范围,然后写入。

但是我发现除了一个响应流外,所有响应流都超时了。 看起来磁盘 I/O 跟不上网络 I/O。但是,如果我做同样的事情,但让每个线程写入一个单独的文件,它就可以正常工作。

作为引用,这是我写入同一个文件的代码:

int numberOfStreams = 4;
List<Tuple<int, int>> ranges = new List<Tuple<int, int>>();
string fileName = @"C:\MyCoolFile.txt";
//List populated here
Parallel.For(0, numberOfStreams, (index, state) =>
{
try
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("Some URL");
using(Stream responseStream = webRequest.GetResponse().GetResponseStream())
{
using (FileStream fileStream = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write))
{
fileStream.Seek(ranges[index].Item1, SeekOrigin.Begin);
byte[] buffer = new byte[64 * 1024];
int bytesRead;
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
if (state.IsStopped)
{
return;
}
fileStream.Write(buffer, 0, bytesRead);
}
}
};
}
catch (Exception e)
{
exception = e;
state.Stop();
}
});

下面是写入多个文件的代码:

int numberOfStreams = 4;
List<Tuple<int, int>> ranges = new List<Tuple<int, int>>();
string fileName = @"C:\MyCoolFile.txt";
//List populated here
Parallel.For(0, numberOfStreams, (index, state) =>
{
try
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("Some URL");
using(Stream responseStream = webRequest.GetResponse().GetResponseStream())
{
using (FileStream fileStream = File.Open(fileName + "." + index + ".tmp", FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write))
{
fileStream.Seek(ranges[index].Item1, SeekOrigin.Begin);
byte[] buffer = new byte[64 * 1024];
int bytesRead;
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
if (state.IsStopped)
{
return;
}
fileStream.Write(buffer, 0, bytesRead);
}
}
};
}
catch (Exception e)
{
exception = e;
state.Stop();
}
});

我的问题是,当从多个线程写入单个文件时,C#/Windows 是否会执行一些额外的检查/操作,这会导致文件 I/O 比写入多个文件时慢?所有磁盘操作都应该受磁盘速度的约束,对吧?谁能解释这种行为?

提前致谢!

更新:这是源服务器抛出的错误:

“无法将数据写入传输连接:连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机没有响应。” [System.IO.IOException]:“无法将数据写入传输连接:连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机未能响应。” InnerException:“连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机未能响应” 消息:“无法将数据写入传输连接:连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机未能响应。” StackTrace:“在 System.Net.Sockets.NetworkStream.Write(Byte[] 缓冲区,Int32 偏移量,Int32 大小)\r\n 在 System.Net.Security._SslStream.StartWriting(Byte[] 缓冲区,Int32 偏移量,Int32 计数, AsyncProtocolRequest asyncRequest)\r\n 在 System.Net.Security._SslStream.ProcessWrite(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)\r\n 在 System.Net.Security.SslStream.Write(Byte[ ] 缓冲区,Int32 偏移量,Int32 计数)\r\n

最佳答案

除非您正在写入 strip 化 RAID,否则您不太可能通过同时从多个线程写入文件来体验性能优势。事实上,它更有可能是相反的——并发写入会交错并导致随机访问,导致磁盘寻道延迟,使它们比大型顺序写入慢几个数量级。

要获得透视感,请看一些 latency comparisons .从磁盘连续读取 1 MB 需要 20 毫秒;写入大约需要相同的时间。另一方面,每次磁盘寻道大约需要 10 毫秒。如果您的写入以 4 KB block 交错,那么您的 1 MB 写入将需要额外的 2560 毫秒寻道时间,使其比顺序写入慢 100 倍。

我建议在任何时候都只允许一个线程写入文件,并且仅将并行性用于网络传输。您可以使用生产者-消费者模式,其中将下载的 block 写入有界并发集合(例如 BlockingCollection<T> ),然后由专用线程拾取并写入磁盘。

关于c# - 写入具有多个流的文件 C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31752022/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com