Cómo: uso AsyncPackage para cargar VSPackages en segundo planoHow to: Use AsyncPackage to load VSPackages in the background

Cargar e inicializar un paquete de VS pueden dar lugar a E/S de disco.Loading and initializing a VS package can result in disk I/O. Si se produce este tipo de E/S en el subproceso de interfaz de usuario, puede provocar problemas de capacidad de respuesta.If such I/O happens on the UI thread, it can lead to responsiveness issues. Para solucionar este problema, Visual Studio 2015 introdujo la AsyncPackage clase que habilita la carga del paquete en un subproceso en segundo plano.To address this, Visual Studio 2015 introduced the AsyncPackage class that enables package loading on a background thread.

Crear un AsyncPackageCreate an AsyncPackage

Puede empezar creando un proyecto VSIX (archivo > New > proyecto > Visual C# > Extensibilidad > proyecto VSIX) y la adición de un VSPackage al proyecto (haga doble clic en el proyecto y agregar > Nuevo elemento > elemento de C# > extensibilidad > Visual El paquete Studio).You can start by creating a VSIX project (File > New > Project > Visual C# > Extensibility > VSIX Project) and adding a VSPackage to the project (right-click on the project and Add > New Item > C# item > Extensibility > Visual Studio Package). A continuación, puede crear sus servicios y agregar esos servicios a su paquete.You can then create your services and add those services to your package.

  1. Derivar el paquete desde AsyncPackage.Derive the package from AsyncPackage.

  2. Si va a proporcionar servicios cuya consulta puede provocar cargue el paquete:If you are providing services whose querying may cause your package to load:

    Para indicar a Visual Studio que el paquete es seguro para la carga en segundo plano y para participar en este comportamiento, su PackageRegistrationAttribute debe establecer AllowsBackgroundLoading propiedad en true en el constructor de atributo.To indicate to Visual Studio that your package is safe for background loading and to opt into this behavior, your PackageRegistrationAttribute should set AllowsBackgroundLoading property to true in the attribute constructor.

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

    Para indicar a Visual Studio que es seguro crear una instancia del servicio en un subproceso en segundo plano, debe establecer el IsAsyncQueryable propiedad en true en el ProvideServiceAttribute constructor.To indicate to Visual Studio that it is safe to instantiate your service on a background thread, you should set the IsAsyncQueryable property to true in the ProvideServiceAttribute constructor.

    [ProvideService(typeof(SMyTestService), IsAsyncQueryable = true)]  
    
  3. Si va a cargar a través de los contextos de interfaz de usuario, a continuación, se debe especificar PackageAutoLoadFlags.BackgroundLoad para el ProvideAutoLoadAttribute o el valor (0 x 2) en las marcas se escribe como el valor de entrada de carga automática de su paquete.If you are loading via UI contexts, then you should specify PackageAutoLoadFlags.BackgroundLoad for the ProvideAutoLoadAttribute OR the value (0x2) into the flags written as the value of your package's auto-load entry.

    [ProvideAutoLoad(UIContextGuid, PackageAutoLoadFlags.BackgroundLoad)]  
    
  4. Si tiene trabajo asincrónico de inicialización, se debe reemplazar InitializeAsync.If you have asynchronous initialization work to do, you should override InitializeAsync. Quitar el Initialize() método proporcionado por la plantilla de VSIX.Remove the Initialize() method provided by the VSIX template. (El Initialize() método AsyncPackage está sellado).(The Initialize() method in AsyncPackage is sealed). Puede usar cualquiera de los AddService métodos para agregar los servicios asincrónicos al paquete.You can use any of the AddService methods to add asynchronous services to your package.

    Nota: Para llamar a base.InitializeAsync(), puede cambiar el código fuente para:NOTE: To call base.InitializeAsync(), you can change your source code to:

    await base.InitializeAsync(cancellationToken, progress);  
    
  5. Debe tener cuidado para no realizar llamadas RPC (llamada a procedimiento remoto) desde el código de inicialización asincrónica (en InitializeAsync).You must take care to NOT make RPCs (Remote Procedure Call) from your asynchronous initialization code (in InitializeAsync). Estos pueden producirse cuando se llama GetService directa o indirectamente.These can occur when you call GetService directly or indirectly. Cuando se requieren las cargas de sincronización, el subproceso de interfaz de usuario se bloqueará con JoinableTaskFactory.When sync loads are required, the UI thread will block using JoinableTaskFactory. El modelo bloqueo predeterminada deshabilita RPC.The default blocking model disables RPCs. Esto significa que si intenta usar una llamada RPC de sus tareas asincrónicas, generará un interbloqueo si el subproceso de interfaz de usuario es esperando cargue el paquete.This means that if you attempt to use an RPC from your async tasks, you will deadlock if the UI thread is itself waiting for your package to load. La alternativa general es calcular las referencias en el código para el subproceso de interfaz de usuario si es necesario usar algo como generador de tareas Joinabledel SwitchToMainThreadAsync o algún otro mecanismo que no usa una RPC.The general alternative is to marshal your code to the UI thread if needed using something like Joinable Task Factory's SwitchToMainThreadAsync or some other mechanism that does not use an RPC. No use ThreadHelper.Generic.Invoke o generalmente bloquea el subproceso que realiza la llamada espera obtener en el subproceso de interfaz de usuario.Do NOT use ThreadHelper.Generic.Invoke or generally block the calling thread waiting to get to the UI thread.

    Nota: Debe evitar el uso GetService o QueryService en su InitializeAsync método.NOTE: You should avoid using GetService or QueryService in your InitializeAsync method. Si tiene que usarlos, deberá cambiar al subproceso de interfaz de usuario en primer lugar.If you have to use those, you will need to switch to the UI thread first. La alternativa es usar GetServiceAsync(Type) desde su AsyncPackage (convirtiéndola en IAsyncServiceProvider.)The alternative is to use GetServiceAsync(Type) from your AsyncPackage (by casting it to IAsyncServiceProvider.)

    C#: Creación de un AsyncPackage:C#: Create an 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);   
    }   
}  

Convertir un VSPackage existente para AsyncPackageConvert an existing VSPackage to AsyncPackage

La mayoría del trabajo es lo mismo que crear un nuevo AsyncPackage.The majority of the work is the same as creating a new AsyncPackage. Siga los pasos 1 a 5 anteriores.Follow steps 1 through 5 above. También debe tener cautela con las siguientes recomendaciones:You also need to take extra caution with the following recommendations:

  1. No olvide quitar el Initialize invalidación que tenía en el paquete.Remember to remove the Initialize override you had in your package.

  2. Evitar los interbloqueos: podría haber oculto de RPC en el código.Avoid deadlocks: There could be hidden RPCs in your code. que tienen lugar en un subproceso en segundo plano.which now happen on a background thread. Asegúrese de que si está realizando una llamada RPC (por ejemplo, GetService), necesita para cambiar al subproceso principal (1) o (2) use la versión asincrónica de la API si existe (por ejemplo, GetServiceAsync).Make sure that if you are making an RPC (for example, GetService), you need to either (1) switch to the main thread or (2) use the asynchronous version of the API if one exists (for example, GetServiceAsync).

  3. No alterne entre los subprocesos con demasiada frecuencia.Do not switch between threads too frequently. Intenta localizar el trabajo que se puede producir en un subproceso en segundo plano para reducir el tiempo de carga.Try to localize the work that can happen in a background thread to reduce the load time.

Consultar los servicios de AsyncPackageQuerying services from AsyncPackage

Un AsyncPackage puede o no puede cargar de forma asincrónica según el autor de llamada.An AsyncPackage may or may not load asynchronously depending on the caller. Por ejemplo,For instance,

  • Si el llamador llamado GetService o QueryService (ambas API sincrónico) oIf the caller called GetService or QueryService (both synchronous APIs) or

  • Si el llamador llamado IVsShell::LoadPackage (o IVsShell5::LoadPackageWithContext) oIf the caller called IVsShell::LoadPackage (or IVsShell5::LoadPackageWithContext) or

  • La carga se desencadena mediante un contexto de interfaz de usuario, pero no especificó que el mecanismo de contexto de interfaz de usuario puede cargar asincrónicamenteThe load is triggered by a UI context, but you did not specify the UI context mechanism can load you asynchronously

    a continuación, el paquete se cargará sincrónicamente.then your package will load synchronously.

    El paquete todavía tiene una oportunidad (en su fase de inicialización asincrónica) para realizar el trabajo fuera del subproceso de interfaz de usuario, aunque se bloqueará el subproceso de interfaz de usuario para la finalización de ese trabajo.Your package still has an opportunity (in its asynchronous initialization phase) to do work off the UI thread, though the UI thread will be blocked for that work's completion. Si el llamador utiliza IAsyncServiceProvider a asincrónicamente la consulta para el servicio, a continuación, la carga e inicialización se hará asincrónicamente suponiendo que no se bloquean inmediatamente en el objeto de la tarea resultante.If the caller uses IAsyncServiceProvider to asynchronously query for your service, then your load and initialization will be done asynchronously assuming they don't immediately block on the resulting task object.

    C#: Cómo consultar el servicio de forma asincrónica:C#: How to query service asynchronously:

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;