写入具有多个流的文件C#
我正在尝试使用HTTP从一台服务器下载一个大文件(>1GB)。为此,我并行地发出HTTP范围请求。这让我可以并行下载该文件 当保存到磁盘时,我会获取每个响应流,打开与文件流相同的文件,寻找我想要的范围,然后写入 然而,我发现除了一个响应流外,所有响应流都超时了。看起来磁盘I/O跟不上网络I/O。但是,如果我做同样的事情,但让每个线程写入一个单独的文件,它就可以正常工作 以下是我在同一文件中编写的代码,以供参考:写入具有多个流的文件C#,c#,multithreading,file,io,parallel.for,C#,Multithreading,File,Io,Parallel.for,我正在尝试使用HTTP从一台服务器下载一个大文件(>1GB)。为此,我并行地发出HTTP范围请求。这让我可以并行下载该文件 当保存到磁盘时,我会获取每个响应流,打开与文件流相同的文件,寻找我想要的范围,然后写入 然而,我发现除了一个响应流外,所有响应流都超时了。看起来磁盘I/O跟不上网络I/O。但是,如果我做同样的事情,但让每个线程写入一个单独的文件,它就可以正常工作 以下是我在同一文件中编写的代码,以供参考: int numberOfStreams = 4; List<Tuple<
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;
列表范围=新列表();
字符串文件名=@“C:\mycolfile.txt”;
//此处填充的列表
并行。对于(0,numberOfStreams,(索引,状态)=>
{
尝试
{
HttpWebRequest webRequest=(HttpWebRequest)webRequest.Create(“某些URL”);
使用(Stream responseStream=webRequest.GetResponse().GetResponseStream())
{
使用(FileStream FileStream=File.Open(fileName,FileMode.OpenOrCreate,FileAccess.Write,FileShare.Write))
{
Seek(范围[index].Item1,SeekOrigin.Begin);
字节[]缓冲区=新字节[64*1024];
int字节读取;
而((bytesRead=responseStream.Read(buffer,0,buffer.Length))>0)
{
如果(state.iss)
{
返回;
}
写入(缓冲区,0,字节读取);
}
}
};
}
捕获(例外e)
{
例外=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();
}
});
int numberOfStreams=4;
列表范围=新列表();
字符串文件名=@“C:\mycolfile.txt”;
//此处填充的列表
并行。对于(0,numberOfStreams,(索引,状态)=>
{
尝试
{
HttpWebRequest webRequest=(HttpWebRequest)webRequest.Create(“某些URL”);
使用(Stream responseStream=webRequest.GetResponse().GetResponseStream())
{
使用(FileStream FileStream=File.Open(fileName+“+index+”.tmp”、FileMode.OpenOrCreate、FileAccess.Write、FileShare.Write))
{
Seek(范围[index].Item1,SeekOrigin.Begin);
字节[]缓冲区=新字节[64*1024];
int字节读取;
而((bytesRead=responseStream.Read(buffer,0,buffer.Length))>0)
{
如果(state.iss)
{
返回;
}
写入(缓冲区,0,字节读取);
}
}
};
}
捕获(例外e)
{
例外=e;
state.Stop();
}
});
我的问题是,C#/Windows在从多个线程写入单个文件时是否会执行一些额外的检查/操作,从而导致文件I/O速度比写入多个文件时慢?所有磁盘操作都应该由磁盘速度绑定,对吗?有人能解释这种行为吗
提前谢谢
更新:以下是源服务器引发的错误:
无法将数据写入传输连接:连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机没有响应
[System.IO.IOException]:“无法将数据写入传输连接:连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机没有响应。”
InnerException:“连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机没有响应”
消息:“无法将数据写入传输连接:连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机没有响应。”
StackTrace:“在System.Net.Sockets.NetworkStream.Write(字节[]缓冲区,Int32偏移量,Int32大小)\r\n在System.Net.Security.\u ssStream.StartWriting(字节[]缓冲区,Int32偏移量,Int32计数,AsyncProtocolRequest异步请求)\r\n在System.Net.Security.\u ssStream.ProcessWrite(字节[])缓冲区,Int32偏移量,Int32计数,AsyncProtocolRequest异步请求)\r\n位于System.Net.Security.SslStream.Write(字节[]缓冲区,Int32偏移量,Int32计数)\r\n除非您正在写入分条RAID,否则您不太可能通过同时从多个线程写入文件来获得性能优势。事实上,更可能的情况是相反的–并发写入将被交错并导致随机访问,从而导致磁盘寻道延迟,从而使它们产生数量级的slo比大的顺序写入更有效 要获得透视感,请看一些。从磁盘连续读取1 MB需要20毫秒;写入大约需要相同的时间。另一方面,每个磁盘寻道大约需要10毫秒。如果您的写入以4 KB块进行交错,那么您的1 MB写入将需要额外的2560毫秒寻道时间,使其比sequenti慢100倍艾尔
我建议在任何时候只允许一个线程写入该文件,并且仅在网络传输时使用并行性。您可以使用生产者-消费者模式,其中下载的块被写入一个有界的并发集合(例如),然后通过专用线程将其提取并写入磁盘。根据目前提供的信息,我猜: 在Windows上,当您写入扩展文件大小的位置时,Windows需要将之前的所有内容初始化为零
fileStream.Seek(ranges[index].Item1, SeekOrigin.Begin);
using System;
using System.IO;
using System.Diagnostics;
class Program {
static void Main(string[] args) {
string path = @"c:\temp\test.bin";
var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write);
fs.Seek(1024L * 1024 * 1024, SeekOrigin.Begin);
var buf = new byte[4096];
var sw = Stopwatch.StartNew();
fs.Write(buf, 0, buf.Length);
sw.Stop();
Console.WriteLine("Writing 4096 bytes took {0} milliseconds", sw.ElapsedMilliseconds);
Console.ReadKey();
fs.Close();
File.Delete(path);
}
}
Writing 4096 bytes took 1491 milliseconds
int numberOfStreams = 4;
List<Tuple<int, int>> ranges = new List<Tuple<int, int>>();
string fileName = @"C:\MyCoolFile.txt";
//Ranges list populated here
using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fileName, FileMode.OpenOrCreate, null, fileSize.Value, MemoryMappedFileAccess.ReadWrite))
{
Parallel.For(0, numberOfStreams, index =>
{
try
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("Some URL");
using(Stream responseStream = webRequest.GetResponse().GetResponseStream())
{
using (MemoryMappedViewStream fileStream = mmf.CreateViewStream(ranges[index].Item1, ranges[index].Item2 - ranges[index].Item1 + 1, MemoryMappedFileAccess.Write))
{
responseStream.CopyTo(fileStream);
}
};
}
catch (Exception e)
{
exception = e;
}
});
}