使用Excel数据的简单任务未完成运行导致“ContextSwitchDeadlock”

时间:2014-05-03 15:40:47

标签: c# excel com async-await com-interop

我有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后,它会显示:

enter image description here

Convert方法调用上面的所有内容,定义如下:

public static async void Convert()
        {

            var excelDataConverter = new ExcelDataConverter(ExcelDataReader.ReadData());
            excelDataConverter.ConvertExelDataAsync();
        }

enter image description here

2 个答案:

答案 0 :(得分:3)

要添加@ StepehCleary的答案,消息本身就非常有用:

  

为避免此问题,所有单线程单元(STA)线程   应该使用抽取等待原语(例如CoWaitForMultipleHandles)   并且在长时间运行期间定期抽取信息。

您有一个进程外Excel Excel对象的COM代理,代理是在您的主线程(可能是STA UI线程)上创建的。然后,您在工作池线程(这是一个MTA线程)上访问它。

虽然COM代理对象本身对于来自这样的工作线程的调用可能是线程安全的,但它很可能试图将调用编组回到最初创建代理的主线程。发生死锁的地方。

为了保持安全,我建议您创建一个专用的STA线程,其中抽取消息,在该线程上创建所有COM对象并在那里呼叫。

我有两个帮助类,ThreadAffinityTaskSchedulerThreadWithAffinityContext,可用here,它们应该适用于任何执行环境。

答案 1 :(得分:2)

当您使用异步和等待时,有一些一般的最佳做法。一个是返回“热门”(正在运行)的任务,因此请勿使用new Task或致电Task.Start;请改用Task.Run。另一个是避免async void;请改用async Task

然而,核心问题是@HansPassant指出:您正在创建STA COM对象(Excel东西),然后在阻塞STA线程后从线程池线程访问它。这注定要失败。

相反,只需删除所有asyncTask代码,然后在STA线程上创建列表。

相关问题