使用Excel数据的简单任务不会导致“ContextSwitchDeadlock”

我有一个Excel workbook对象包含一张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 ,说:

托pipedebugging助手'ContextSwitchDeadlock'已经在'C:\ Users \ Aymen \ Documents \ Visual Studio 2013 \ Projects \ WebGraphMaker \ WebGraphMaker \ bin \ Debug \ WebGraphMaker.vshost.exe'中检测到问题。

附加信息:CLR已经无法从COM上下文0xd33a5e78转换到COM上下文0xd33a5fa0 60秒。 拥有目的地上下文/公寓的线程很可能是在不抽取Windows消息的情况下进行非抽取等待,或者处理非常长的运行操作。 这种情况通常会对性能产生负面影响,甚至可能导致应用程序无响应或内存使用量不断累积。 为了避免这个问题,所有的单线程单元(STA)线程应该使用抽取等待原语(比如CoWaitForMultipleHandles),并在长时间运行的操作中定期抽取消息。

注意 :这是我第一次与Com对象交互。

更新1

当Excel不在任务中时,Excel消耗很好,一旦进入任务,任务开始,问题就会发生!

更新2 :debugging时,一旦debugging器到达线路

 int rowCount = _worksheetRange.Rows.Count; 

它退出并没有发生,不能解释它。

更新3 :打开debugging> Windows>线程后,它显示:

在这里输入图像描述

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

 public static async void Convert() { var excelDataConverter = new ExcelDataConverter(ExcelDataReader.ReadData()); excelDataConverter.ConvertExelDataAsync(); } 

在这里输入图像说明

要添加到@ StepehCleary的答案,消息本身是非常丰富的:

为了避免这个问题,所有的单线程单元(STA)线程应该使用抽取等待原语(比如CoWaitForMultipleHandles),并在长时间运行的操作中定期抽取消息。

你有一个COM代理到一个EXCEL的COM COM对象,这个代理是在你的主线程(可能是一个STA UI线程)上创build的。 然后你正在一个工作者池线程(这是一个MTA线程)访问它。

尽pipeCOM代理对象本身对于像这样的工作者线程的调用可能是线程安全的,但在底层,它最有可能试图将调用callback到最初创build代理的主线程。 这就是发生死锁的地方。

为了保持安全,我build议你创build一个专门的STA线程来抽取消息 ,在那个线程上创build所有的COM对象,然后在那里调用。

我有两个这样的助手类, ThreadAffinityTaskSchedulerThreadWithAffinityContext ,可以在这里find ,他们应该在任何执行环境中工作。

当你使用asynchronous和等待时,有一些一般的最佳实践。 一个是返回“热”(运行)的任务,所以不要使用new Task或调用Task.Start ; 使用Task.Run代替。 另一个是避免async void ; 改用async Task

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

相反,只要删除所有的asyncTask代码,并在STA线程上创build您的列表。