C# 为什么使用Task.Run()仍然阻塞我的UI?

C# 为什么使用Task.Run()仍然阻塞我的UI?,c#,async-await,C#,Async Await,我使用Task.Run()调用了一个同步方法,我的UI被阻止且没有响应。该方法通过COM互操作从数据库加载信息,我对此没有任何控制权 public List<EdmAddInInfo2> GetInstalledAddins() { IEdmAddInMgr7 addinMgr = m_vault as IEdmAddInMgr7; Array installedAddins = Array.CreateInstance(typeof(EdmAddInInfo2),

我使用Task.Run()调用了一个同步方法,我的UI被阻止且没有响应。该方法通过COM互操作从数据库加载信息,我对此没有任何控制权

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;
}
公共列表GetInstalledAddins() { IEdmAddInMgr7 addinMgr=m_vault作为IEdmAddInMgr7; Array installedAddins=Array.CreateInstance(typeof(EdmAddInInfo2),0); addinMgr.GetInstalledAddIns(out installedAddins); 如果(已安装尺寸?.Length>0) 返回installedAddins.OfType().ToList(); 返回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;
}
private async void LicensesForm_显示(对象发送方,事件参数e)
{        
var m_addins=await getinstalleddins().ConfigureAwait(false);
toolStripStatusLabel2.Text=$“已加载{m_addins.Count}个加载项。”;
}
专用异步任务GetInstalledAddins()
{
AddinManager addinMgr=新的AddinManager(Vault);
var addins=await Task.Run(()=>addinMgr.GetInstalledAddins()).ConfigureAwait(false);
返回addins;
}

通常我会使用BCW并在路上,但我想我会给任务一个机会。有什么想法吗?

在评论中讨论之后,我认为涉及COM的一些更深层次的事情正在发生。涉及COM时需要注意的一件事是,它使用Dispatcher来接收事件,这在以前让我感到痛苦。如果这个问题与COM有关,人们可能需要更多关于正在发生什么的信息,挖掘可能不值得。我希望我能提供更多的帮助,但我想我不得不默认为简单的解决方法提供建议。启动一个线程来调用GetInstalledAddins,将结果分配给一个局部变量,并通过Dispatcher通知UI完成

另外,从我编辑前的原始答案中添加上述内容

var m_addins = await GetInstalledAddins().ConfigureAwait(false);
应该是:

var m_addins = await GetInstalledAddins().ConfigureAwait(true);
这是因为在下一行中,您指定给UI元素的文本属性。此分配必须从UI线程完成,当您调用
GetInstalledAddins()
时,UI线程处于活动状态,但因为您随后调用
ConfigureAwait(false)
异步管理器(我忘了它的调用)选择的任何线程上的
await
后,执行将继续


在UI代码中使用
async
/
await
的优点之一是(在正常情况下)可以在调用
await
的同一线程上恢复执行。这样,您就可以在
wait
之后继续访问UI对象。但是对
ConfigureAwait(false)
的调用指示
async
/
await
引擎,您不关心在哪个线程上恢复执行(但在这种情况下,您确实应该关心在同一线程上恢复执行)

在评论中进行了讨论之后,我认为涉及COM的事情有更深层次的发展。涉及COM时需要注意的一件事是,它使用Dispatcher来接收事件,这在以前让我感到痛苦。如果这个问题与COM有关,人们可能需要更多关于正在发生什么的信息,挖掘可能不值得。我希望我能提供更多的帮助,但我想我不得不默认为简单的解决方法提供建议。启动一个线程来调用GetInstalledAddins,将结果分配给一个局部变量,并通过Dispatcher通知UI完成

另外,从我编辑前的原始答案中添加上述内容

var m_addins = await GetInstalledAddins().ConfigureAwait(false);
应该是:

var m_addins = await GetInstalledAddins().ConfigureAwait(true);
这是因为在下一行中,您指定给UI元素的文本属性。此分配必须从UI线程完成,当您调用
GetInstalledAddins()
时,UI线程处于活动状态,但因为您随后调用
ConfigureAwait(false)
异步管理器(我忘了它的调用)选择的任何线程上的
await
后,执行将继续

在UI代码中使用
async
/
await
的优点之一是(在正常情况下)可以在调用
await
的同一线程上恢复执行。这样,您就可以在
wait
之后继续访问UI对象。但是对
ConfigureAwait(false)
的调用指示
async
/
await
引擎,您不关心在哪个线程上恢复执行(但在这种情况下,您确实应该关心在同一线程上恢复执行)

该方法通过COM互操作从数据库加载信息,我对此没有任何控制权

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;
}
那么,根据该方法的实现,可能无法解除对UI线程的阻塞

但是,您可以尝试这样做:如果该类型在其构造函数中分配COM对象,那么它们可能会绑定到UI线程。我将尝试在后台线程上创建实例:

private Task<List<EdmAddInInfo2>> GetInstalledAddins()
{
  return Task.Run(() => new AddinManager(Vault).GetInstalledAddins());
}
私有任务GetInstalledAddins()
{
返回Task.Run(()=>newaddinManager(Vault).GetInstalledAddins());
}
通常我会使用BCW并在路上

BackgroundWorker
也会遇到同样的问题

该方法通过COM互操作从数据库加载信息,我对此没有任何控制权

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;
}
那么,根据该方法的实现,可能无法解除对UI线程的阻塞

但是,您可以尝试这样做:如果该类型在其构造函数中分配COM对象,那么它们可能会绑定到UI线程。我将尝试在后台线程上创建实例:

private Task<List<EdmAddInInfo2>> GetInstalledAddins()
{
  return Task.Run(() => new AddinManager(Vault).GetInstalledAddins());
}
私有任务GetInstalledAddins()
{
返回Task.Run(()=>newaddinManager(Vault).GetInstalledAddins());
}
通常我会使用BCW并在路上


BackgroundWorker
也会有同样的问题。

您是从UI线程调用GetInstalledAddins()吗?是的。它是从LicensesForm_Ushow()调用的,如上面所示。对于GRIN,请在GetInstalledAddins()中返回一个空列表。也许构造器是需要花费时间的东西