我有一个我用Task.Run()调用的同步方法,我的UI被阻止且没有响应。该方法通过COM Interop从数据库加载信息,我对此没有任何控制。
public List<EdmAddInInfo2> GetInstalledAddins()
{
IEdmAddInMgr7 addinMgr = m_vault as IEdmAddInMgr7;
Array installedAddins = Array.CreateInstance(typeof(EdmAddInInfo2), 0);
addinMgr.GetInstalledAddIns(out installedAddins);
if (installedAddins?.Length > 0)
return installedAddins.OfType<EdmAddInInfo2>().ToList();
return null;
}
我在显示表单时以这种方式调用方法;
private async void LicensesForm_Shown(object sender, EventArgs e)
{
var m_addins = await GetInstalledAddins().ConfigureAwait(false);
toolStripStatusLabel2.Text = $"Loaded {m_addins.Count} addins.";
}
private async Task<List<EdmAddInInfo2>> GetInstalledAddins()
{
AddinManager addinMgr = new AddinManager(Vault);
var addins = await Task.Run(() => addinMgr.GetInstalledAddins()).ConfigureAwait(false);
return addins;
}
通常我会使用BCW并且在路上,但我想我会给任务一个机会。有什么想法吗?
答案 0 :(得分:3)
在评论讨论之后,我认为有更深层次的涉及COM的事情。涉及COM时要注意的一件事是它使用Dispatcher来接收事件,这些事件之前已经让我感到困惑。如果问题与COM有关,那么可能需要更多有关正在发生的事情的信息以及挖掘可能不值得。我希望我能提供更多帮助,但我认为我必须默认提供简单的方法。启动线程以调用GetInstalledAddins,将结果分配给局部变量,并通过Dispatcher通知UI完成。
另外,从编辑之前的原始答案添加上面的内容,
var m_addins = await GetInstalledAddins().ConfigureAwait(false);
应该是:
var m_addins = await GetInstalledAddins().ConfigureAwait(true);
这是因为在下一行中您指定了UI元素的Text属性。此分配必须从UI线程完成,该线程在您调用GetInstalledAddins()
时处于活动状态,但是因为您随后调用ConfigureAwait(false)
,在await
之后,在异步管理器的任何线程上继续执行(我忘记了)被选中的是什么。
在UI代码中使用async
/ await
的优势之一是执行可以(在正常情况下)在进行await
调用的同一线程上恢复。这样,您可以在await
之后继续访问UI对象。但是,您对ConfigureAwait(false)
的调用指示async
/ await
引擎您不关心执行恢复的线程(但在这种情况下,您真的应该关心执行恢复在同一个线程上)。
答案 1 :(得分:1)
该方法通过COM Interop从数据库加载信息,我对此无法控制。
那么,根据该方法的实现,可能可能取消阻止UI线程。
但是,您可以尝试这样做:如果该类型在其构造函数中分配COM对象,则它们可能与UI线程绑定。我会尝试在后台线程上创建实例:
private Task<List<EdmAddInInfo2>> GetInstalledAddins()
{
return Task.Run(() => new AddinManager(Vault).GetInstalledAddins());
}
通常我会使用BCW并在路上
BackgroundWorker
会遇到完全相同的问题。