修改 Roslyn 工作区的并发性? Visual studio是怎么做到的?

Concurrency of modifying a Roslyn workspace? How does Visual studio do it?

可能是个愚蠢的问题,但是:是否有任何方法可以可靠地将更改同时应用到 Roslyn 工作区?如果不是,确保按正确顺序完成的最佳做法是什么?

示例:假设您已将一些解决方案加载到工作区中,并且具有将新项目添加到解决方案的基本功能:

private Workspace _workspace;

// This is not thread-safe, right?
void AddProject()
{
  var project = _workspace.CurrentSolution.AddProject(/* ... */);
  _workspace.TryApplyChanges(project.Solution);
}

第一个问题:如果错了请指正,但我认为 AddProject 不是线程安全的,对吗?

例如,假设您要同时添加新项目。所以你同时调用了两次 AddProject()。

我的理解是存在竞争条件,您最终可能会添加两个项目(如果其中一个调用在另一个调用到达 _workspace.CurrentSolution 之前完成 TryApplyChanges),或者只添加了一个项目(如果在执行 TryApplyChanges 之前两个调用都已达到 _worksapce.CurrentSolution)。

第二个问题:如果我的理解是正确的,那么有没有最好的方法来避免这些并发问题?我想唯一真正的方法是 schedule/execute 每个修改顺序,对吧?

例如,Visual Studio 是如何做到这一点的。仅在 Dispatcher 上完成?

谢谢

底层代码是线程安全的,但您的用法不是。

如果解决方案在此期间发生变化,

TryApplyChanges() 将 return false。如果发生这种情况,您需要再次尝试更改(从新的 CurrentSolution 开始)。

基本上,你需要写

Solution changed;
do {
  changed = _workspace.CurrentSolution....();
} while (!_workspace.TryApplyChanges(changed);

这称为比较和交换循环;有关详细信息,请参阅 my blog