Udostępnij za pośrednictwem


Ładowanie pakietów VSPackage w tle za pomocą pakietu AsyncPackage

Ładowanie i inicjowanie pakietu programu VS może spowodować we/wy dysku. Jeśli takie we/wy wystąpią w wątku interfejsu użytkownika, może to prowadzić do problemów z czasem reakcji. Aby rozwiązać ten problem, program Visual Studio 2015 wprowadził klasę AsyncPackage umożliwiającą ładowanie pakietów w wątku w tle.

Tworzenie pakietu AsyncPackage

Możesz rozpocząć od utworzenia projektu VSIX (Plik>nowy>projekt Visual C#>Rozszerzalność>VSIX)> i dodania pakietu VSPackage do projektu (kliknij prawym przyciskiem myszy projekt i Dodaj>nowy>element C# rozszerzalność>pakietu> Visual Studio). Następnie możesz utworzyć usługi i dodać te usługi do pakietu.

  1. Utwórz pakiet z klasy AsyncPackage.

  2. Jeśli udostępniasz usługi, których wykonywanie zapytań może spowodować załadowanie pakietu:

    Aby wskazać programOwi Visual Studio, że pakiet jest bezpieczny do ładowania w tle i aby zdecydować się na to zachowanie, PackageRegistrationAttribute ustaw właściwość AllowsBackgroundLoading na wartość true w konstruktorze atrybutu.

    [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
    
    

    Aby wskazać programOwi Visual Studio, że utworzenie wystąpienia usługi w wątku w tle jest bezpieczne, należy ustawić IsAsyncQueryable właściwość na true w konstruktorze ProvideServiceAttribute .

    [ProvideService(typeof(SMyTestService), IsAsyncQueryable = true)]
    
    
  3. W przypadku ładowania za pomocą kontekstów interfejsu użytkownika należy określić wartość PackageAutoLoadFlags.BackgroundLoad dla ProvideAutoLoadAttribute wartości OR (0x2) na flagi zapisane jako wartość wpisu automatycznego ładowania pakietu.

    [ProvideAutoLoad(UIContextGuid, PackageAutoLoadFlags.BackgroundLoad)]
    
    
  4. Jeśli masz asynchroniczną pracę inicjaliza, należy zastąpić InitializeAsyncelement . Usuń metodę dostarczoną Initialize() przez szablon VSIX. (Metoda Initialize() w AsyncPackage jest zapieczętowana). Do dodawania usług asynchronicznych do pakietu można użyć dowolnej AddService metody.

    UWAGA: Aby wywołać base.InitializeAsync()metodę , możesz zmienić kod źródłowy na:

    await base.InitializeAsync(cancellationToken, progress);
    
  5. Należy zachować ostrożność, aby nie wykonywać wywołań procedury zdalnej z kodu inicjowania asynchronicznego (w pliku InitializeAsync). Mogą one wystąpić w przypadku bezpośredniego lub pośredniego wywołania GetService . Gdy wymagane są obciążenia synchronizacji, wątek interfejsu użytkownika będzie blokowany przy użyciu polecenia JoinableTaskFactory. Domyślny model blokowania wyłącza wywołania żądań ściągnięć. Oznacza to, że jeśli próbujesz użyć wywołania procedury RPC z zadań asynchronicznych, zakleszczenia, jeśli wątek interfejsu użytkownika sam czeka na załadowanie pakietu. Ogólna alternatywa polega na przesłaniu kodu do wątku interfejsu użytkownika, jeśli jest to konieczne, przy użyciu funkcji sprzężenia do fabrykiSwitchToMainThreadAsync zadań lub innego mechanizmu, który nie korzysta z procedury RPC. Nie używaj threadHelper.Generic.Invoke lub zazwyczaj blokuj wątek wywołujący oczekując na dostęp do wątku interfejsu użytkownika.

    UWAGA: Należy unikać używania metody GetService lub QueryServiceInitializeAsync. Jeśli musisz użyć tych elementów, musisz najpierw przełączyć się do wątku interfejsu użytkownika. Alternatywą jest użycie z GetServiceAsync pakietu AsyncPackage (przez rzutowanie go do elementu IAsyncServiceProvider.

    C#: Tworzenie pakietu AsyncPackage:

[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[ProvideService(typeof(SMyTestService), IsAsyncQueryable = true)]
public sealed class TestPackage : AsyncPackage
{
    protected override Task InitializeAsync(System.Threading.CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
    {
        this.AddService(typeof(SMyTestService), CreateService, true);
        return Task.FromResult<object>(null);
    }
}

Konwertowanie istniejącego pakietu VSPackage na pakiet AsyncPackage

Większość pracy jest taka sama jak tworzenie nowego pakietu AsyncPackage. Wykonaj kroki od 1 do 5 powyżej. Należy również zachować szczególną ostrożność z następującymi zaleceniami:

  1. Pamiętaj, aby usunąć Initialize przesłonięcia w pakiecie.

  2. Unikaj zakleszczeń: w kodzie mogą istnieć ukryte karty RPC. które teraz mają miejsce w wątku w tle. Upewnij się, że jeśli tworzysz RPC (na przykład GetService), musisz przełączyć się na główny wątek lub (2) użyć asynchronicznej wersji interfejsu API, jeśli istnieje (na przykład GetServiceAsync).

  3. Nie przełączaj się zbyt często między wątkami. Spróbuj zlokalizować pracę, która może wystąpić w wątku w tle, aby skrócić czas ładowania.

Wykonywanie zapytań dotyczących usług z pakietu AsyncPackage

Pakiet AsyncPackage może lub nie może być ładowany asynchronicznie w zależności od elementu wywołującego. Na przykład

  • Jeśli obiekt wywołujący o nazwie GetService lub QueryService (oba synchroniczne interfejsy API) lub

  • Jeśli obiekt wywołujący o nazwie IVsShell::LoadPackage (lub IVsShell5::LoadPackageWithContext) lub

  • Obciążenie jest wyzwalane przez kontekst interfejsu użytkownika, ale nie określono mechanizmu kontekstu interfejsu użytkownika, który można załadować asynchronicznie

    pakiet zostanie załadowany synchronicznie.

    Pakiet nadal ma możliwość (w fazie inicjowania asynchronicznego), aby wykonać pracę poza wątkiem interfejsu użytkownika, choć wątek interfejsu użytkownika zostanie zablokowany na potrzeby ukończenia tej pracy. Jeśli obiekt wywołujący używa elementu IAsyncServiceProvider do asynchronicznego wykonywania zapytań dotyczących usługi, obciążenie i inicjowanie zostaną wykonane asynchronicznie przy założeniu, że nie będą natychmiast blokować wynikowego obiektu zadania.

    C#: Jak wykonywać zapytania dotyczące usługi asynchronicznie:

using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;

IAsyncServiceProvider asyncServiceProvider = Package.GetService(typeof(SAsyncServiceProvider)) as IAsyncServiceProvider;
IMyTestService testService = await asyncServiceProvider.GetServiceAsync(typeof(SMyTestService)) as IMyTestService;