假设下面的方法被.net 4应用程序中的不同线程调用了数千次。处理这种情况的最好方法是什么?我知道磁盘是这里的瓶颈,但我希望WriteFile()方法能快速返回。
数据大小可以高达几MB。我们是在谈论线程池、第三方公共关系还是类似的东西?
public void WriteFile(string FileName, MemoryStream Data)
{
try
{
using (FileStream DiskFile = File.OpenWrite(FileName))
{
Data.WriteTo(DiskFile);
DiskFile.Flush();
DiskFile.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}发布于 2011-09-15 03:38:01
既然您说文件不需要按顺序或立即写入,那么最简单的方法就是使用Task
private void WriteFileSynchronous(string FileName, MemoryStream Data)
{
Task.Factory.StartNew(() => WriteFileSynchronously(FileName, Data));
}
private void WriteFileSynchronous(string FileName, MemoryStream Data)
{
try
{
using (FileStream DiskFile = File.OpenWrite(FileName))
{
Data.WriteTo(DiskFile);
DiskFile.Flush();
DiskFile.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}TPL在内部使用线程池,即使对于大量任务也应该相当有效。
发布于 2011-09-15 03:29:04
如果你想快速返回,而不是真正关心操作是否同步,你可以在内存中创建某种Queue,你将在其中放置写请求,当队列没有填满时,你可以快速从方法返回。另一个线程将负责调度Queue和写入文件。如果您的WriteFile被调用并且队列已满,您将不得不等待,直到您可以排队,并且执行将再次变得同步,但是通过这种方式,您可能有一个很大的缓冲区,所以如果处理文件写入请求不是线性的,而是更尖峰的(写入文件调用高峰之间的暂停),这样的改变可以被视为您的性能的改进。
更新:为你做了一张小照片。请注意,瓶颈总是存在的,您所能做的就是使用队列优化请求。请注意,队列是有限制的,因此当它被填满时,您无法将队列文件安装到中,您必须等待,以便缓冲区中也有空闲空间。但是对于图片中呈现的情况(3个存储桶请求),很明显你可以快速地将存储桶放入队列并返回,而在第一种情况下,你必须一个接一个地执行并阻塞执行。
请注意,你永远不需要一次执行许多IO线程,因为它们都将使用相同的瓶颈,如果你尝试大量并行,你只会浪费内存,我相信2- 10个线程顶部将轻松地占用所有可用的IO带宽,并将限制应用程序内存的使用。

发布于 2011-09-15 07:49:53
如果数据传入的速度快于您记录它的速度,那么您就有了一个真正的问题。一种生产者/消费者设计,让WriteFile只是把东西扔到ConcurrentQueue或类似的结构中,并且有一个单独的线程为队列提供服务,效果很好……直到队列填满。如果您正在谈论打开50,000个不同的文件,那么备份速度会很快。更不用说每个文件可能有几兆字节的数据,这将进一步限制队列的大小。
我遇到过一个类似的问题,我通过将WriteFile方法附加到一个文件中解决了这个问题。它写入的记录有一个记录号、文件名、长度,然后是数据。正如Hans在对原始问题的评论中指出的那样,写入文件很快,打开文件很慢。
我的程序中的第二个线程开始读取WriteFile正在写入的文件。该线程读取每个记录头(编号、文件名、长度),打开一个新文件,然后将数据从日志文件复制到最终文件。
如果日志文件和最终文件位于不同的磁盘上,这会工作得更好,但它仍然可以在单个磁盘轴上很好地工作。不过,它肯定会锻炼你的硬盘。
它的缺点是需要2倍的磁盘空间,但由于2TB的硬盘低于150美元,我认为这不是什么大问题。总体来说,它的效率也低于直接写入数据(因为您必须处理两次数据),但它的好处是不会导致主处理线程停止。
https://stackoverflow.com/questions/7421797
复制相似问题