C# 同时从不同客户端流式传输大数据
这是一个bit架构和代码问题。我有很多源url,其中包含来自许多不同客户端的巨大文件,我必须下载这些文件并保存在文件系统中 我对RAM有硬件限制。因此,我想以字节块的形式缓冲每个流,并且我认为为流的每次下载启动一个线程是个好主意 我添加了使用任务并行库启动线程/任务的代码,如下所示:C# 同时从不同客户端流式传输大数据,c#,multithreading,asynchronous,streaming,task-parallel-library,C#,Multithreading,Asynchronous,Streaming,Task Parallel Library,这是一个bit架构和代码问题。我有很多源url,其中包含来自许多不同客户端的巨大文件,我必须下载这些文件并保存在文件系统中 我对RAM有硬件限制。因此,我想以字节块的形式缓冲每个流,并且我认为为流的每次下载启动一个线程是个好主意 我添加了使用任务并行库启动线程/任务的代码,如下所示: public Task RunTask(Action action) { Task task = Task.Run(action); return task; } 我通过以下方法传递动作参数:
public Task RunTask(Action action)
{
Task task = Task.Run(action);
return task;
}
我通过以下方法传递动作参数:
public void DownloadFileThroughWebStream(WebClient webClient, Uri src, string dest, long buffersize)
{
Stream stream = webClient.OpenRead(src);
byte[] buffer = new byte[buffersize];
int len;
using (BufferedStream bufferedStream = new BufferedStream(stream))
{
using (FileStream fileStream = new FileStream(Path.GetFullPath(dest), FileMode.Create, FileAccess.Write))
{
while ((len = stream.Read(buffer, 0, buffer.Length)) > 0)
{
fileStream.Write(buffer, 0, len);
fileStream.Flush();
}
}
}
}
出于测试目的,我尝试通过为每个特定下载启动线程/任务,从http uri下载一些资源:
[Test]
public async Task DownloadSomeStream()
{
Uri uri = new Uri("http://mirrors.standaloneinstaller.com/video-sample/metaxas-keller-Bell.mpeg");
List<Uri> streams = new List<Uri> { uri, uri, uri};
List<Task> tasks = new List<Task>();
var path = "C:\\TMP\\";
//Create task for each of the streams from uri
int c = 1;
foreach (var uri in streams)
{
WebClient webClient = new WebClient();
Task task = taskInitiator.RunTask(() => DownloadFileThroughWebStream(webClient, uri, Path.Combine(path,"File"+c), 8192));
tasks.Add(task);
c++;
}
Task allTasksHaveCompleted = Task.WhenAll(tasks);
await allTasksHaveCompleted;
}
在线:
using (FileStream fileStream = new FileStream(Path.GetFullPath(dest), FileMode.Create, FileAccess.Write))
除了这个例外,有两件事我不明白:
System.IO.IOException: 'The process cannot access the file 'D:\TMP\File4' because it is being used by another process'
为什么不允许写?另一个进程是如何分配文件的
我只添加了3个url,所以我只应该有文件:file1、file2和file3,为什么它要保存file4
另外,还有其他一些问题,可以让我们思考一下:
就我想要实现的目标而言,我所做的是正确的吗?我使用任务并行库启动任务是否正确
有什么提示和技巧、最佳实践等吗
我们可以创建可以执行下载的下载方法:
async Task DownloadFile(string url, string location, string fileName)
{
using (var client = new WebClient())
{
await client.DownloadFileTaskAsync(url, $"{location}{fileName}");
}
}
并且上面的方法可以被Task调用。Run执行文件的同步下载:
IList<string> urls = new List<string>()
{
@"http://mirrors.standaloneinstaller.com/video-sample/metaxas-keller-Bell.mpeg",
@"https://...",
@"https://..."
};
string location = "D:";
Directory.CreateDirectory(location);
Task.Run(async () =>
{
var tasks = urls.Select(url =>
{
var fileName = url.Substring(url.LastIndexOf('/'));
return DownloadFile(url, location, fileName);
}).ToArray();
await Task.WhenAll(tasks);
}).GetAwaiter().GetResult();
为什么不允许写?另一个过程是如何分配的
档案
文件未被其他进程锁定,而是被同一进程锁定。如果你打开一个文件进行写操作,你基本上会得到一个独占锁。当您尝试再次打开该文件以从另一个任务写入时,它将被锁定,这就是您出现错误的原因
要处理这种情况,您应该在将数据写入磁盘时设置一个锁。对于要写入的每个唯一文件名,都应该有一个单独的锁对象,并且要小心使用正确的锁
当我只添加了3个url时,为什么它要保存文件4,所以我只
应该有文件:file1、file2和file3
这是因为您在传递给Task.Run的委托中捕获了变量c。由于这些任务通常在循环结束后启动,因此c的值现在为4。有关闭包的更多信息,请参见