假设下面的方法被.net 4应用程序中的不同线程调用了几千次。处理这种情况的最佳方法是什么?了解磁盘是这里的瓶颈,但我希望WriteFile()方法快速返回。
数据最多可以达到几MB。我们在谈论线程池,TPL等吗?
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);
}
}
答案 0 :(得分:6)
如果你想快速返回而不是真的关心那个操作是同步的,你可以创建某种内存Queue
来放置写请求,而Queue没有填满你可以从方法返回很快。另一个线程将负责调度Queue
和编写文件。如果你的WriteFile
被调用并且队列已满,你将不得不等到你可以排队并且执行将再次变为同步,但是这样你可以有一个大的缓冲区,所以如果进程文件写请求不是线性的,但是更尖刻的是(在写文件调用尖峰之间暂停)这种变化可以看作是你性能的提高。
<强>更新强> 为你做了一个小图片。请注意,瓶颈始终存在,您可以做的就是使用队列优化请求。请注意,队列有限制,所以当它填满时,你不能将文件队列成inst,你必须等待,因此该缓冲区中也有一个空闲空间。但是对于图片中呈现的情况(3个桶请求),显而易见的是,您可以快速将桶放入队列并返回,而在第一种情况下,您必须逐个执行此操作并阻止执行。
请注意,您永远不需要一次执行多个IO线程,因为它们都将使用相同的瓶颈而且如果您尝试将这种情况大量并行,那么您将浪费内存,我相信2到10个线程顶部会占用所有可用的线程IO带宽很容易,也会限制应用程序内存的使用。
答案 1 :(得分:3)
既然你说文件不需要按顺序写入,那么最简单的方法就是使用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在内部使用线程池,即使对于大量任务也应该相当有效。
答案 2 :(得分:1)
如果数据的传输速度超过了您的记录速度,那么您就会遇到真正的问题。生成器/消费者设计,WriteFile
只是将内容放入ConcurrentQueue
或类似的结构中,并且为该队列提供服务的单独线程运行良好......直到队列填满为止。如果您正在谈论打开50,000个不同的文件,那么事情就会快速备份。更不用说每个文件的数据可能是几兆字节,这将进一步限制队列的大小。
我有一个类似的问题,我通过将WriteFile
方法附加到单个文件来解决。它写的记录有记录号,文件名,长度,然后是数据。汉斯在对原始问题的评论中指出,写入文件很快; 打开文件很慢。
我程序中的第二个线程开始读取WriteFile
正在写入的文件。该线程读取每个记录头(数字,文件名,长度),打开一个新文件,然后将数据从日志文件复制到最终文件。
如果日志文件和最终文件位于不同的磁盘上,则效果会更好,但是对于单个主轴,它仍然可以正常工作。但它肯定能锻炼你的硬盘。
它的缺点是需要2倍的磁盘空间,但是在150美元以下的2TB驱动器中,我不认为这是一个很大的问题。它总体上比直接写入数据效率低(因为你必须处理两次数据),但它的好处是不会导致主处理线程停止。
答案 3 :(得分:0)
将完整的方法实现封装在新的Thread()
中。然后你可以“发射并忘记”这些线程并返回主调用线程。
foreach (file in filesArray)
{
try
{
System.Threading.Thread updateThread = new System.Threading.Thread(delegate()
{
WriteFileSynchronous(fileName, data);
});
updateThread.Start();
}
catch (Exception ex)
{
string errMsg = ex.Message;
Exception innerEx = ex.InnerException;
while (innerEx != null)
{
errMsg += "\n" + innerEx.Message;
innerEx = innerEx.InnerException;
}
errorMessages.Add(errMsg);
}
}