VSTRD010:访问项目应该只在主线程上完成

Sar*_*ger 5 c# multithreading asynchronous vsix visual-studio-2019

Microsoft 将 VSIX 更改为与 VS2019 异步,随之而来的是悲伤。

我看到很多警告说:

警告 VSTHRD010 访问“项目”应该只在主线程上完成。首先调用 Microsoft.VisualStudio.ProjectSystem.IProjectThreadingService.VerifyOnUIThread()。

我确实添加了,虽然想删除 Nuget 包Microsoft.VisualStudio.ProjectSystem,但没有帮助。

将该行添加到方法的顶部会产生:

错误 CS0120 非静态字段、方法或属性“IProjectThreadingService.VerifyOnUIThread()”需要对象引用

搜索对解决最后一个错误没有帮助。

我尝试添加:

`Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();

但这并没有改变事情。

想法?

更新

我认为接近投票是来自说提供代码的用户。没有“代码”。任何从 VS2019 之前升级到 VS2019 并遇到同步 VSIX 到异步 VSIX 的人都会理解这个问题。不能像以前那样只使用物品。由于 VS 现在将许多元素仅分类为“主 UI 可访问”,因此出现了许多此类警告。这意味着

if (null == oItems || 0 == oItems.Count)
   return false;
Run Code Online (Sandbox Code Playgroud)

不再有效。oItems 定义为 EnvDTE。ProjectItems. Hence the warning访问 ProjectItems . Basically, anything inEnvDTE` 是不受限制的。

代码将是任何接触/使用 EnvDTE 对象的东西。

对答案 1 的回应

我实现了答案并抛出了一个新错误。

private void MenuItemCallback(object sender, EventArgs e)
{
info VSTHRD102 -> this.packageVsi.JoinableTaskFactory.Run(async () =>
    {
        await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
        this.EventOptionsDialog();
    });
}

private Task EventOptionsDialog()
{
    _ = System.Threading.Tasks.Task.Run(async () =>
    {
        await this.packageVsi.DoTask1Async();
    }
    );

    // Other tasks

    return null;
}
Run Code Online (Sandbox Code Playgroud)

返回的信息性消息是:

严重性 VSTHRD102 将同步阻塞方法调用(例如 JoinableTaskFactory.Run 或 Task.Result)的使用限制为必须同步的公共入口点成员。将其用于内部成员可能会在异步帧之间不必要地添加同步帧,从而导致线程池耗尽。

  1. 我想要干净的工作代码,所以没有消息、警告或错误。
  2. 有很多代码需要解决方案,所以如果这种技术耗尽了线程池,那么这个答案就不好。
  3. 有很多下游方法,据我观察,VS2019 很愚蠢,但不知道子方法已经拥有主 UI。这个过程有点复杂,因为一些方法是从多个位置调用的。
  4. 我仍然想要干净的代码,而不需要添加许多新的代码块。

Jim*_*mmy 5

当迁移到 AsyncPackage 或其他新的异步上下文时,如果您不在主线程上,切换到主线程可能比断言/抛出更容易。这通常通过 await 来完成JoinableTaskFactory.SwitchToMainThreadAsync()

JoinableTaskFactory继承自AsyncPackage,或者也可以通过 访问Microsoft.VisualStudio.Shell.ThreadHelper

如果你在一个不再在 UI 线程上的同步方法中并希望委托给它,一个常见的模式是:

// Fire-and-forget
JoinableTaskFactory.RunAsync(async () =>
{
    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
    DoWork();
});

// Synchronously block the current thread and wait.
// Not recommended - can lead to thread pool starvation if over-used.
JoinableTaskFactory.Run(async () =>
{
    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
    DoWork();
});
Run Code Online (Sandbox Code Playgroud)

可以在VS Threading Cookbook 中找到有关此主题的更多信息。

编辑:对回应的回应......

VSTHRD102 错误来自使用同步等待。正如我所提到的,JTF.Run()在 UI 线程上完成工作时会阻塞当前线程(task.Wait()task.Result)。基本上你消耗了 2 个线程,而实际上只使用了一个线程,因此分析器会抱怨(这样做太频繁了,线程池将被耗尽,因为所有线程最终都处于同步阻塞状态)。

VS 中推出的线程设计更改将导致您:

  • 将您的方法转换为async Task<T>以便您可以等待JTF.RunAsync()(或JTF.SwitchToMainThreadAsync()直接等待)。
  • 使用JTF.RunAsync()非异步方法将您的代码转换为通过即发即弃风格的任务运行。
  • 禁止这些分析器警告。请仅将此作为仔细考虑的最后手段,因为它们旨在减轻在 VS 中产生性能问题或挂起的常见情况。

说到 VS 中的挂起,在示例代码的 EventOptionsDialog 方法中,您正在使用System.Threading.Tasks.Task.Run(). 这在 VS(或者可能是任何其他具有 UI 线程问题的应用程序)中也可能是危险的,因为 Task.Run 不支持重入。例如,如果 UI 线程上的某些内容等待Task.Run(您没有等待,但只是为了示例),这将阻塞 UI 线程,并且如果委托的工作尝试在 UI 线程上安排更多工作,会死锁挂VS。使用 JTF 将缓解这个问题,因为它确实在某种程度上跟踪了重入场景。