找回密码
 立即注册

QQ登录

只需一步,快速开始

sunyuanze

注册会员

21

主题

66

帖子

169

积分

注册会员

积分
169

活字格认证微信认证勋章

sunyuanze
注册会员   /  发表于:2010-11-1 13:28  /   查看:8264  /  回复:7
产品版本: 3.0J
产品模块:读取模板
操作系统:WindowsXP SP3
IDE:VS2005
问题描述:
发生如下异常:
FarPoint.Web.SpreadJ:
Error Opening file
   場所 FarPoint.Web.Spread.FpSpread.a(String A_0, Int32 A_1, String A_2,
Int32 A_3, String A_4, ExcelWarningList A_5)
   場所 FarPoint.Web.Spread.FpSpread.OpenExcel(String fileName)
   場所 Kobetsu.Business.CSinseisyoBusiness.SGInitTemplate() 場所
D:\Source\Kobetsu\Business\clsSinseisyoBusiness.vb:行 608
FarPoint.Web.SpreadJ:
Error Verifying file
   場所 d.b(String A_0)
   場所 d.a(String A_0, Stream A_1, Int32 A_2, String A_3, Int32 A_4, String A_5, ExcelOpenFlags A_6, ExcelWarningList A_7)
mscorlib:
別のプロセスで使用されているため、プロセスはファイル
'C:\SpreadTemplate\Template.xls' にアクセスできません。

重现步骤:
建立2个线程,同时循环1000次读取同一个模板

相关代码:
for i as integer =0 to 1000
    Me.Spread.OpenExcel("C:\SpreadTemplate\Template.xls")
next

7 个回复

倒序浏览
winking
葡萄城公司职员   /  发表于:2010-11-1 14:25:00
沙发
首先从lz的异常信息可以知道当前的问题:
“別のプロセスで使用されているため、プロセスはファイル”
-(对于)->
“The process cannot access the file 'C:\SpreadTemplate\Template.xls' because it is being used by another process.”
这是一个IO异常导致,在参考lz的示例,显然多个线程“正在同时”操作文件'C:\SpreadTemplate\Template.xls',从而导致异常。

实际上,lz只需要下面的代码就可以重现问题。
  1. for i as integer =0 to 1000
  2.     System.IO.File.Open("C:\SpreadTemplate\Template.xls")
  3. next
复制代码
综合以上,这不是Spread的问题,而是文件IO不支持并发访问,如果lz需要多线程加速的话,可以使用下面的方式:
1、使用File.Open(String)获得文件的所有二进制数据作为cache
2、在每个线程访问1中的数据拷贝,得到MemoryStream
3、调用Spread.OpenExcel(Stream)来读取模板

参考代码:
  1. byte[] templateDataBuffer;
  2. using (System.IO.FileStream fs = System.IO.File.OpenRead(@"C:\SpreadTemplate\Template.xls"))
  3. {
  4.     templateDataBuffer = new byte[fs.Length];
  5.     fs.Read(templateDataBuffer, 0, (int)fs.Length);
  6. }
  7. ParameterizedThreadStart ts = new ParameterizedThreadStart(delegate(object p)
  8.     {
  9.         byte[] buffer = p as byte[];
  10.         if (buffer != null)
  11.         {
  12.             for (int i = 0; i < 1000; i++)
  13.             {
  14.                 using (System.IO.Stream s = new System.IO.MemoryStream(buffer))
  15.                 {
  16.                     ss.OpenExcel(s);
  17.                 }
  18.             }
  19.         }
  20.     });
  21. Thread t1 = new Thread(ts);
  22. Thread t2 = new Thread(ts);

  23. t1.Start(templateDataBuffer);
  24. t2.Start(templateDataBuffer);
复制代码
回复 使用道具 举报
sunyuanze
注册会员   /  发表于:2010-11-1 14:58:00
板凳
感谢楼上的回答:)
因为是一个asp.net的程序(网站),发布到IIS服务器上的。
并不是想用多线程实现提速。。。
当多个人同时打开用spread做成的画面的时候,就可能发生同时读取模板的问题

我就是想问问,spread除了提供OpenExcel方法外,是否提供可以加同步锁功能的函数,以保证同一时间不能有多个线程访问1个模板。
回复 使用道具 举报
gw0506
超级版主   /  发表于:2010-11-1 16:05:00
地板
是这样,Spread中没有加同步锁功能的函数。这与控件本身的一些设计惯例有关。
不过Spread提供了一个重载的OpenExcel方法带有ExcelWarningList类型的参数,这个参数可以捕获Open excel文件时的异常。例如:

  1. string f;
  2. f = &quot;D:\openexcel.xls&quot;;
  3. System.IO.FileStream s = new System.IO.FileStream(f, IO.FileMode.Open, IO.FileAccess.ReadWrite);
  4. if (!IsPostBack)
  5. {
  6.    FarPoint.Excel.ExcelWarning w = new FarPoint.Excel.ExcelWarningList();
  7.    FpSpread1.OpenExcel(s, w);
  8. }
复制代码
基于这个接口,你看能不能这样:首先捕获ExcelWarning,根据不同的warning,你可以选择在后台生成临时文件,或者通知用户等待的操作并重试。这是可以进一步执行后续操作。
回复 使用道具 举报
sunyuanze
注册会员   /  发表于:2010-11-1 17:28:00
5#
感谢回答:)

恩,这样做就比较复杂了

既然spread自身没有提供同步锁的功能,
那就在代码中使用SyncLock就可以了:)
回复 使用道具 举报
winking
葡萄城公司职员   /  发表于:2010-11-1 17:49:00
6#
原帖由 sunyuanze 于 2010-11-1 14:58:00 发表
感谢楼上的回答:)
因为是一个asp.net的程序(网站),发布到IIS服务器上的。
并不是想用多线程实现提速。。。
当多个人同时打开用spread做成的画面的时候,就可能发生同时读取模板的问题

我就是想问问,spread除了提供OpenExcel方法外,是否提供可以加同步锁功能的函数,以保证同一时间不能有多个线程访问1个模板。


这是一个问题,Spread中没有这样的机制,因为它取决于系统的文件IO。
简单的解决方是使用File.Open(string path, FileMode mode, FileAccess access, FileShare share)来解决(当然前提是只读)
  1. using (System.IO.Stream s = System.IO.File.Open(filename, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read))
  2.             {
  3.                 ss.OpenExcel(s);
  4.             }
复制代码
回复 使用道具 举报
winking
葡萄城公司职员   /  发表于:2010-11-1 18:00:00
7#
原帖由 sunyuanze 于 2010-11-1 14:58:00 发表
感谢楼上的回答:)
因为是一个asp.net的程序(网站),发布到IIS服务器上的。
并不是想用多线程实现提速。。。
当多个人同时打开用spread做成的画面的时候,就可能发生同时读取模板的问题

我就是想问问,spread除了提供OpenExcel方法外,是否提供可以加同步锁功能的函数,以保证同一时间不能有多个线程访问1个模板。


另外,把上面我提到的方法稍稍变形一下就可以解决问题,同时还能提高性能,但是消耗内存为代价的。
封装了一下,使用下面的ConvurrentOpen代替File.OpenRead即可,当然如果需要其他方式,可以自己修改。
  1. private static Dictionary<string, byte[]> dataBuffer;
  2. private static readonly object dataBufferCreateSyncRoot = new object();
  3. private static readonly object dataBufferReadSyncRoot = new object();
  4. public static System.IO.Stream ConcurrentOpen(string fileName)
  5. {
  6.     try
  7.     {
  8.         byte[] data = null;

  9.         if (dataBuffer == null)// buffer is not existed
  10.         {
  11.             lock (dataBufferCreateSyncRoot)
  12.             {
  13.                 if (dataBuffer == null)
  14.                 {
  15.                     Dictionary<string, byte[]> buffer = new Dictionary<string, byte[]>();

  16.                     lock (dataBufferReadSyncRoot) // lock to read file
  17.                     {
  18.                         using (System.IO.Stream s = System.IO.File.OpenRead(fileName))
  19.                         {
  20.                             data = new byte[s.Length];
  21.                             s.Read(data, 0, (int)s.Length); // Read to end.
  22.                             buffer[fileName] = data;
  23.                         }
  24.                         dataBuffer = buffer;
  25.                     }
  26.                 }
  27.             }
  28.         }
  29.         else
  30.         {
  31.             lock (dataBufferReadSyncRoot)
  32.             {
  33.                 // buffer is already exist
  34.                 if (!dataBuffer.TryGetValue(fileName, out data))
  35.                 {
  36.                     using (System.IO.Stream s = System.IO.File.OpenRead(fileName))
  37.                     {
  38.                         data = new byte[s.Length];
  39.                         s.Read(data, 0, (int)s.Length); // Read to end.
  40.                         dataBuffer[fileName] = data;
  41.                     }
  42.                 }
  43.             }
  44.         }

  45.         return new System.IO.MemoryStream(data);
  46.     }
  47.     catch
  48.     {
  49.         return null;
  50.     }
  51. }
复制代码
回复 使用道具 举报
gw0506
超级版主   /  发表于:2010-11-1 18:00:00
8#
Winking重现江湖~
感谢Winking强力支持!!
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 立即注册
返回顶部