我有Excel
workbook
个对象,其中包含一个sheet
,我想将其内容复制到List
。
我有这个方法:
private Task GeneratePagesList()
{
_pages = new List<Model.Page>();
short idCount = 0;
var generatePagesListTask = new Task(() =>
{
_pages.Add(new Model.Page()
{
Url = new Uri(_worksheetRange.Cells[i, j].Value2.ToString(),
UriKind.RelativeOrAbsolute),
Id = idCount
});
});
return generatePagesListTask;
}
现在我想使用此方法和它返回的Task
,如下所示:
public async void ConvertExelDataAsync()
{
var generatePagesListTask = GeneratePagesList();
generatePagesListTask.Start();
await generatePagesListTask;
}
当我跑步时,操作花费的时间太长,并且它永远不会退出ConvertExelDataAsync
方法,过了一会儿(显然是60秒),我收到一个Exception
说:
托管调试助手&#39; ContextSwitchDeadlock&#39;已在C:\ Users \ Aymen \ Documents \ Visual Studio中检测到问题 2013 \项目\ WebGraphMaker \ WebGraphMaker \ BIN \调试\ WebGraphMaker.vshost.exe&#39;
附加信息:CLR无法从COM上下文0xd33a5e78过渡到COM上下文0xd33a5fa0 60秒。该 最有可能拥有目标上下文/公寓的线程 要么是非抽运等待,要么处理很长时间 没有抽取Windows消息的操作。这种情况一般 具有负面的性能影响,甚至可能导致应用程序 变得无响应或内存使用不断累积 时间。为了避免这个问题,所有单线程公寓(STA) 线程应该使用抽取等待原语(例如 CoWaitForMultipleHandles)并且在很长时间内定期泵送消息 正在运行。
注意:这是我第一次与Com对象进行交互。
更新1 :
当Excel不在任务中,一旦在任务中,并且任务开始时,Excel消耗就会正常工作,就会出现问题!
更新2 :调试时,一旦调试器到达
行int rowCount = _worksheetRange.Rows.Count;
退出并且什么也没发生,无法解释。
更新3 : 打开Debug&gt; Windows&gt; Threads后,它会显示:
Convert方法调用上面的所有内容,定义如下:
public static async void Convert()
{
var excelDataConverter = new ExcelDataConverter(ExcelDataReader.ReadData());
excelDataConverter.ConvertExelDataAsync();
}
答案 0 :(得分:3)
要添加@ StepehCleary的答案,消息本身就非常有用:
为避免此问题,所有单线程单元(STA)线程 应该使用抽取等待原语(例如CoWaitForMultipleHandles) 并且在长时间运行期间定期抽取信息。
您有一个进程外Excel Excel对象的COM代理,代理是在您的主线程(可能是STA UI线程)上创建的。然后,您在工作池线程(这是一个MTA线程)上访问它。
虽然COM代理对象本身对于来自这样的工作线程的调用可能是线程安全的,但它很可能试图将调用编组回到最初创建代理的主线程。发生死锁的地方。
为了保持安全,我建议您创建一个专用的STA线程,其中抽取消息,在该线程上创建所有COM对象并在那里呼叫。
我有两个帮助类,ThreadAffinityTaskScheduler
和ThreadWithAffinityContext
,可用here,它们应该适用于任何执行环境。
答案 1 :(得分:2)
当您使用异步和等待时,有一些一般的最佳做法。一个是返回“热门”(正在运行)的任务,因此请勿使用new Task
或致电Task.Start
;请改用Task.Run
。另一个是避免async void
;请改用async Task
。
然而,核心问题是@HansPassant指出:您正在创建STA COM对象(Excel东西),然后在阻塞STA线程后从线程池线程访问它。这注定要失败。
相反,只需删除所有async
和Task
代码,然后在STA线程上创建列表。