C# Excel.Application 多线程 Static 静态

6lilu9 2020-12-09 07:45:59
最近想鼓捣一个项目,就是从一堆Excel文件(大概500+)中读取数据;
方法很基础,我已封装成个了类DoInExcel(见文末代码);
过程中发现执行这句代码速度太慢,
Excel.Application xlApp = new Excel.Application()

于是想了两个方法:
第一个方法思路就是只建一个类,循环打开关闭;
第二个方法就是多线程;
这两个方法都很有效;第二个方法速度更快一点;

第三个方法想混合第一个和第二个方法,但却出现了错误,提示“消息筛选器显示应用程序正在使用中。

请高手帮忙看看什么原因。

另外我还有个突发其想,既然运行慢的原因是每次都初始化一下xlApp,那直接把DoInExcel类中的这句代码改为
  static Excel.Application xlApp = new Excel.Application();

可不可行呢?
剧透一下:我测试了一下(当然是注释掉//dE2.Quit();再测试),在方法一中可行的,但在方法二(多线程中)不可行,也不知什么原因。




using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Excel = Microsoft.Office.Interop.Excel;

namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{

Do();
Console.ReadKey();
}

static async void Do()
{
string Path = @"G:\22\2020";



List<FileInfo> OrFileList1 = new DirectoryInfo(Path).GetFiles().ToList();

Stopwatch sw = new Stopwatch();

Console.WriteLine("经典常规操作:也是最费时的操作,10个文件耗时为:");
sw.Start();//结束计时
foreach (var item in OrFileList1)
{
DoInExcel dE0 = new DoInExcel(false);
dE0.Open(item.FullName);
string ss = dE0.GetDate();
dE0.Close();
dE0.Quit();
}
sw.Stop();
Console.WriteLine(sw.Elapsed);

Console.WriteLine("方法一:只建一个类,循环打开关闭");
sw.Reset();
sw.Start();//结束计时
DoInExcel dE = new DoInExcel(false);
foreach (var item in OrFileList1)
{
dE.Open(item.FullName);
string ss = dE.GetDate();
dE.Close();

}
dE.Quit();
sw.Stop();
Console.WriteLine(sw.Elapsed);

Console.WriteLine("--------------------------------------------");
Console.WriteLine("方法二:多线程,同样10个文件耗时为:");
sw.Reset();
sw.Start();
List<Task> TaskList = new List<Task>();
foreach (var item in OrFileList1)
{
TaskList.Add(Task.Run(() =>
{
DoInExcel dE2 = new DoInExcel(false);
dE2.Open(item.FullName);
string ss = dE2.GetDate();
dE2.Close();
dE2.Quit();
}));
}
await Task.WhenAll(TaskList.ToArray());
sw.Stop();
Console.WriteLine(sw.Elapsed);

Console.WriteLine("--------------------------------------------");
Console.WriteLine("方法三:方法一+方法二,Open语句有故障");
sw.Reset();
sw.Start();
List<Task> TaskList3 = new List<Task>();
DoInExcel dE3 = new DoInExcel(false);
foreach (var item in OrFileList1)
{
TaskList3.Add(Task.Run(() =>
{
dE3.Open(item.FullName);
string ss = dE3.GetDate();
dE3.Close();
}));

}
await Task.WhenAll(TaskList3.ToArray());
dE3.Quit();
sw.Stop();
Console.WriteLine(sw.Elapsed);

}

}

public class DoInExcel
{
Excel.Application xlApp = new Excel.Application();//这代码太耽误时间了
Excel.Workbook oWbook;
Excel.Worksheet oWSheet;


public DoInExcel(bool ViseFlag = true)
{
xlApp.Visible = ViseFlag;
}

public void Open(string fileFullName)
{
oWbook = xlApp.Workbooks.Open(fileFullName);
oWSheet = oWbook.Sheets[1];
}

public string GetDate()
{
return oWSheet.get_Range("A1").Value;
}

public void WritDate(string Str)
{
oWSheet.get_Range("A1").Value = Str;
}

public void Close(bool savechange = true)
{
oWSheet = null;

oWbook.Close(savechange);
oWbook = null;
}
public void Quit()
{
xlApp.DisplayAlerts = true;

if (oWbook != null) this.Close();

if (xlApp != null)
{
xlApp.Workbooks.Close();
xlApp.Quit();
xlApp = null;
}
}

}

}


...全文
21224 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
泡泡龙 2020-12-26
  • 打赏
  • 举报
回复
引用 16 楼 wllllll 的回复:
[quote=引用 15 楼 泡泡龙 的回复:][quote=引用 14 楼 wllllll 的回复:]2楼和3楼的回答不要看,每次都是打非所问。 Excel.Application是创建一个隐藏窗体的EXCLE.EXE来执行你想要执行的操作,所以慢是很正常的,想要快,一般是EXCLE.EXE打开一个文件,再另存为NPOI容易识别的文件类型,再通过NPOI来识别读取。 而且Excel.Application打开的EXCLE.EXE,容易出现BUG,导致程序不能正常退出,你还需要写个进程监控程序,处理未能正常关闭的EXCLE.EXE
那不是bug,是你写的托管代码没有释放引用的非托管com对象,导致Excel无法退出[/quote] 那你想释放就觉得能退出而已。10年前就用OFFICE 2003开发过了。那时候没有NPOI。服务器进程经常无法退出,如果你发送的指令需要执行太久。这时候再释放,虽然代码显示已经释放,但是进程是关不掉的了。有时候是其他的excel弹窗导致无法退出。反正多了去了。[/quote] 你应该认真看一下微软的开发文档,学习一下ReleaseComObject的使用前提和条件 https://www.cnblogs.com/Charltsing/p/RealeaseComObject.html
wllllll 2020-12-26
  • 打赏
  • 举报
回复
引用 15 楼 泡泡龙 的回复:
[quote=引用 14 楼 wllllll 的回复:]2楼和3楼的回答不要看,每次都是打非所问。 Excel.Application是创建一个隐藏窗体的EXCLE.EXE来执行你想要执行的操作,所以慢是很正常的,想要快,一般是EXCLE.EXE打开一个文件,再另存为NPOI容易识别的文件类型,再通过NPOI来识别读取。 而且Excel.Application打开的EXCLE.EXE,容易出现BUG,导致程序不能正常退出,你还需要写个进程监控程序,处理未能正常关闭的EXCLE.EXE
那不是bug,是你写的托管代码没有释放引用的非托管com对象,导致Excel无法退出[/quote] 那你想释放就觉得能退出而已。10年前就用OFFICE 2003开发过了。那时候没有NPOI。服务器进程经常无法退出,如果你发送的指令需要执行太久。这时候再释放,虽然代码显示已经释放,但是进程是关不掉的了。有时候是其他的excel弹窗导致无法退出。反正多了去了。
泡泡龙 2020-12-26
  • 打赏
  • 举报
回复
引用 14 楼 wllllll 的回复:
2楼和3楼的回答不要看,每次都是打非所问。 Excel.Application是创建一个隐藏窗体的EXCLE.EXE来执行你想要执行的操作,所以慢是很正常的,想要快,一般是EXCLE.EXE打开一个文件,再另存为NPOI容易识别的文件类型,再通过NPOI来识别读取。 而且Excel.Application打开的EXCLE.EXE,容易出现BUG,导致程序不能正常退出,你还需要写个进程监控程序,处理未能正常关闭的EXCLE.EXE
那不是bug,是你写的托管代码没有释放引用的非托管com对象,导致Excel无法退出
wllllll 2020-12-25
  • 打赏
  • 举报
回复
2楼和3楼的回答不要看,每次都是打非所问。 Excel.Application是创建一个隐藏窗体的EXCLE.EXE来执行你想要执行的操作,所以慢是很正常的,想要快,一般是EXCLE.EXE打开一个文件,再另存为NPOI容易识别的文件类型,再通过NPOI来识别读取。 而且Excel.Application打开的EXCLE.EXE,容易出现BUG,导致程序不能正常退出,你还需要写个进程监控程序,处理未能正常关闭的EXCLE.EXE
weixin_42406391 2020-12-23
  • 打赏
  • 举报
回复
用这个COM打开慢 ,退出还一堆问题
泡泡龙 2020-12-22
  • 打赏
  • 举报
回复
首先,Excel是个STA,所以你不能乱写多线程读。 其次,Excel打开500+的Excel文件是很慢的,如果你有大量的纯数据读写,不要使用Com方式,应该用NPOI或者EPPlus之类的第三方 最后,如果一定要用Excel来操作的话,建议串行打开文件,使用Object[,]数组一次性读取数据,很快的。
enaking 2020-12-18
  • 打赏
  • 举报
回复
如果要速度应该读xml。支持excel。
dugupiaoyun 2020-12-15
  • 打赏
  • 举报
回复 1
为什么不试试用读数据库的方式来读excel文件呢? strConn = "Provider=Microsoft.Ace.OleDb.12.0;Data Source="Excel文件地址";Extended Properties='Excel 8.0;HDR=NO;IMEX=1';"; OleDbConnection conn = new OleDbConnection(strConn); conn.Open(); string sql = "select F1,F2,F3,F4,F5,F6,F7,F8 from [" + sheetName + "]";//"select * from [sheet1$]"; OleDbDataAdapter adp = new OleDbDataAdapter(sql, strConn); DataSet ds = new DataSet(); adp.Fill(ds); conn.Close();
Denuin 2020-12-15
  • 打赏
  • 举报
回复
用Microsoft.Office.Interop.Excel注定慢, 还不如学习其它使用OpenXML方式的类库。
6lilu9 2020-12-15
  • 打赏
  • 举报
回复
引用 8 楼 dugupiaoyun 的回复:
为什么不试试用读数据库的方式来读excel文件呢? strConn = "Provider=Microsoft.Ace.OleDb.12.0;Data Source="Excel文件地址";Extended Properties='Excel 8.0;HDR=NO;IMEX=1';"; OleDbConnection conn = new OleDbConnection(strConn); conn.Open(); string sql = "select F1,F2,F3,F4,F5,F6,F7,F8 from [" + sheetName + "]";//"select * from [sheet1$]"; OleDbDataAdapter adp = new OleDbDataAdapter(sql, strConn); DataSet ds = new DataSet(); adp.Fill(ds); conn.Close();
这个很容易出错~
  • 打赏
  • 举报
回复
再次强调一下,尽量保证用
uiControl.Invoke(delegate{ ......} )
的方式保证阻塞到 UI 主线程中才去访问 Excel。
  • 打赏
  • 举报
回复
COM/DCOM 通常不能在子线程中正常运行,你可能某个功能“瞬间”过去了,其实根本没正常执行,而90%的其它功能则反而“卡死”进程(例如原本500毫秒执行完的功能总是用20秒钟才能执行完)。

全都用 UI 主线程调用 Office 的 COM 对象。尽量使用你的第一种方式。
cihn2 2020-12-12
  • 打赏
  • 举报
回复
要么就空间换时间,事先new,实例化100个xlApp或者更多的实例放入数组, 然后同时处理
cihn2 2020-12-12
  • 打赏
  • 举报
回复
到github找个excel封装好的库,绕开COM
6lilu9 2020-12-12
  • 打赏
  • 举报
回复
引用 2 楼 以专业开发人员为伍 的回复:
COM/DCOM 通常不能在子线程中正常运行,你可能某个功能“瞬间”过去了,其实根本没正常执行,而90%的其它功能则反而“卡死”进程(例如原本500毫秒执行完的功能总是用20秒钟才能执行完)。 全都用 UI 主线程调用 Office 的 COM 对象。尽量使用你的第一种方式。
真是感谢....,“你可能某个功能“瞬间”过去了,其实根本没正常执行,而90%的其它功能则反而“卡死”进程”确实是
6lilu9 2020-12-12
  • 打赏
  • 举报
回复
引用 3 楼 以专业开发人员为伍 的回复:
再次强调一下,尽量保证用
uiControl.Invoke(delegate{ ......} )
的方式保证阻塞到 UI 主线程中才去访问 Excel。
这一段没听懂;但第一段的“尽量使用你的第一种方式。”听懂了 但500个文件太慢了....,要两三个小时呢...
ziqi0716 2020-12-10
  • 打赏
  • 举报
回复

110,539

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧