/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/Core/Def/LanguageService/PackageLoadTasks.cs
74 строки
3 KB
Todd Grunke
Switch OnAfterPackageLoadedAsync to use a task queue (#77737)
21 мар 2025, 23:25
Не верифицирован
21 мар 2025, 23:25
8e70d6b
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService; using WorkTask = Func<PackageLoadTasks, CancellationToken, Task>; /// <summary> /// Provides a mechanism for registering work to be done during package initialization. Work is registered /// as either main thread or background thread appropriate. This allows processing of these work items /// in a batched manner, reducing the number of thread switches required during the performance sensitive /// package loading timeframe. /// /// Note that currently the processing of these tasks isn't done concurrently. A future optimization may /// allow parallel background thread task execution, or even concurrent main and background thread work. /// </summary> internal sealed class PackageLoadTasks(JoinableTaskFactory jtf) { private readonly ConcurrentQueue<WorkTask> _backgroundThreadWorkTasks = []; private readonly ConcurrentQueue<WorkTask> _mainThreadWorkTasks = []; private readonly JoinableTaskFactory _jtf = jtf; public void AddTask(bool isMainThreadTask, WorkTask task) { var workTasks = GetWorkTasks(isMainThreadTask); workTasks.Enqueue(task); } public async Task ProcessTasksAsync(CancellationToken cancellationToken) { // prime the pump by doing the first group of bg thread work if the initiating thread is not the main thread if (!_jtf.Context.IsOnMainThread) await PerformWorkAsync(isMainThreadTask: false, cancellationToken).ConfigureAwait(false); // Continue processing work until everything is completed, switching between main and bg threads as needed. while (!_mainThreadWorkTasks.IsEmpty || !_backgroundThreadWorkTasks.IsEmpty) { await PerformWorkAsync(isMainThreadTask: true, cancellationToken).ConfigureAwait(false); await PerformWorkAsync(isMainThreadTask: false, cancellationToken).ConfigureAwait(false); } } private ConcurrentQueue<WorkTask> GetWorkTasks(bool isMainThreadTask) => isMainThreadTask ? _mainThreadWorkTasks : _backgroundThreadWorkTasks; private async Task PerformWorkAsync(bool isMainThreadTask, CancellationToken cancellationToken) { var workTasks = GetWorkTasks(isMainThreadTask); if (workTasks.IsEmpty) return; // Ensure we're invoking the task on the right thread if (isMainThreadTask) await _jtf.SwitchToMainThreadAsync(cancellationToken); else if (_jtf.Context.IsOnMainThread) await TaskScheduler.Default; while (workTasks.TryDequeue(out var work)) { // CA(true) is important here, as we want to ensure that each iteration is done in the same // captured context. Thus, even poorly behaving tasks (ie, those that do their own thread switching) // don't effect the next loop iteration. await work(this, cancellationToken).ConfigureAwait(true); } } }