使用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对象,然后在那里调用。
我有两个这样的助手类, ThreadAffinityTaskScheduler
和ThreadWithAffinityContext
,可以在这里find ,他们应该在任何执行环境中工作。
当你使用asynchronous和等待时,有一些一般的最佳实践。 一个是返回“热”(运行)的任务,所以不要使用new Task
或调用Task.Start
; 使用Task.Run
代替。 另一个是避免async void
; 改用async Task
。
然而,核心问题是@HansPassant指出:你正在创build一个STA COM对象(Excel的东西),然后阻塞STA线程后从线程池线程访问它。 这注定会失败。
相反,只要删除所有的async
和Task
代码,并在STA线程上创build您的列表。