Asp.net异步文件读写并发处理总结
运行场景
系统在用户打开下载页面的时候异步请求,根据数据动态生成一个Word文件,并返回给页面文件的大小显示在页面上,用户在生成后可以点击下载获取生成的Word文件,不希望数据有超过5分钟的延迟。
遇到的问题
一开始很简单的写了几句代码,在本地测试通过。省去业务逻辑的代码如下
生成Word:
protected int CreateWord()
{
string strFilePath = @"D:\NewWord.docx";
ProjectWord word = new ProjectWord();
word.Save(strFilePath);
System.IO.FileInfo file = new System.IO.FileInfo(strFilePath);
return file.Length;
}
下载Word:
Response.ContentType = "application/x-zip-compressed";
Response.AddHeader("Content-Disposition", "attachment;filename=" + Path.GetFileName(strFilePath));
Response.TransmitFile(strFilePath);
Response.Flush();
Response.End();
上传到服务器的时候,大量的操作就出问题了。在word.Save的时候出现了IO读写错误,想了想是文件被用户下载的时候,给占用的,读写不能同时操作,怎么办呢?
方案一
生成Word的代码:
protected int CreateWord()
{
string strFilePath = @"D:\NewWord.docx";
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
ProjectWord word = new ProjectWord();
int i = 0;
while (i < 10)
{
try
{
word.Save(strFilePath);
}
catch
{
System.Threading.Thread.Sleep(1000);
i++;
}
}
}));
t.Start();
System.IO.FileInfo file = new System.IO.FileInfo(strFilePath);
return file.Length;
}
下载Word代码不变。
网站不在报错了,用户也能正常下载了文件,但下载出来的文件有时候不是及时的,原因太简单了,保存10次都没成功的话,就不保存了,而且并发量大的时候是几乎不会成功的。只适合小并发量的情况下使用该方法,小并发量我也不推荐,虽然我使用了这个方法顶了几个小时。
方案二:
生成Word的代码:
protected int CreateWord()
{
string strFilePath = @"D:\NewWord.docx";
lock (brCreaterAddressLock)
{
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
brCreaterAddressLock.AddParticipant();
ProjectWord word = new ProjectWord();
int i = 0;
while (i <= 10)
{
try
{
word.Save(strFilePath); }
catch
{
}
finally
{
brCreaterAddressLock.RemoveParticipant();
}
}
}));
t.Start();
}
System.IO.FileInfo file = new System.IO.FileInfo(strFilePath);
return file.Length;
}
public static System.Threading.Barrier brCreaterAddressLock = new System.Threading.Barrier(0);
下载Word的代码:
Handler.brCreaterAddressLock.AddParticipant();
Handler.brCreaterAddressLock.SignalAndWait();
Response.ContentType = "application/x-zip-compressed";
Response.AddHeader("Content-Disposition", "attachment;filename=" + Path.GetFileName(strFilePath));
Response.TransmitFile(strFilePath);
Response.Flush();
Response.End();
该方案运行良好,但下载的响应速度有点慢,在生成的时候,下载的线程需要等生成的线程结束后才能运行。这样对用户的来说不太好,虽然word的生成也只是几秒钟,而且是异步生成文件,页面上有个转圈圈的图片给用户看。
方案三
生成Word的代码:
protected int CreateWord()
{
if (brCreaterAddressLock.ParticipantCount == 0)
{
lock (brCreaterAddressLock)
{
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
brCreaterAddressLock.AddParticipant();
ProjectWord word = new ProjectWord();
int i = 0;
while (i <= 10)
{
try
{
word.Save(@"D:\NewWord.docx");
}
catch
{
}
finally
{
brCreaterAddressLock.RemoveParticipant();
isCreatingAddress = false;
}
}
}));
t.Start();
}
}
System.IO.FileInfo file = new System.IO.FileInfo(strFilePath);
return file.Length;
}
public static System.Threading.Barrier brCreaterAddressLock = new System.Threading.Barrier(0);
下载Word的代码:
Handler.brCreaterAddressLock.AddParticipant();
Handler.brCreaterAddressLock.SignalAndWait();
Handler.brCreaterAddressLock.RemoveParticipant();//要注意这句,SignalAndWait()不会帮你移除一个参与者,如果没这句的话,在生成的时候 if (brCreaterAddressLock.ParticipantCount == 0)这句就只会运行一次
Response.ContentType = "application/x-zip-compressed";
Response.AddHeader("Content-Disposition", "attachment;filename=" + Path.GetFileName(strFilePath));
Response.TransmitFile(strFilePath);
Response.Flush();
Response.End();
这样的话,用户在文件生成中的时候就会给他直接旧版文件的大小,加快了异步的响应速度,提高了用户体验。
<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>