Compartir a través de


Uso de AsyncPackage para cargar VSPackages en segundo plano

La carga e inicialización de un paquete de VS puede dar lugar a la E/S de disco. Si este tipo de E/S se produce en el subproceso de la interfaz de usuario, puede provocar problemas de capacidad de respuesta. Para solucionar esto, Visual Studio 2015 introdujo la clase que habilita la AsyncPackage carga de paquetes en un subproceso en segundo plano.

Creación de un AsyncPackage

Para empezar, cree un proyecto VSIX (Archivo>nuevo>>proyecto visual C#>Extensibilidad>del proyecto VSIX) y agregue un VSPackage al proyecto (haga clic con el botón derecho en el proyecto y Agregue>nuevo>elemento>de C# Extensibilidad>del paquete de Visual Studio). Después, puede crear los servicios y agregar esos servicios al paquete.

  1. Derive el paquete de AsyncPackage.

  2. Si proporciona servicios cuya consulta puede hacer que el paquete se cargue:

    Para indicar a Visual Studio que el paquete es seguro para la carga en segundo plano y para participar en este comportamiento, PackageRegistrationAttribute debe establecer la propiedad AllowsBackgroundLoading en true en el constructor de atributos.

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

    Para indicar a Visual Studio que es seguro crear instancias del servicio en un subproceso en segundo plano, debe establecer la IsAsyncQueryable propiedad en true en el ProvideServiceAttribute constructor.

    [ProvideService(typeof(SMyTestService), IsAsyncQueryable = true)]
    
    
  3. Si va a cargar a través de contextos de interfaz de usuario, debe especificar PackageAutoLoadFlags.BackgroundLoad para or ProvideAutoLoadAttribute el valor (0x2) en las marcas escritas como el valor de la entrada de carga automática del paquete.

    [ProvideAutoLoad(UIContextGuid, PackageAutoLoadFlags.BackgroundLoad)]
    
    
  4. Si tiene trabajo de inicialización asincrónica que realizar, debe invalidar InitializeAsync. Quite el Initialize() método proporcionado por la plantilla VSIX. (El Initialize() método de AsyncPackage está sellado). Puede usar cualquiera de los AddService métodos para agregar servicios asincrónicos al paquete.

    NOTA: Para llamar a base.InitializeAsync(), puede cambiar el código fuente a:

    await base.InitializeAsync(cancellationToken, progress);
    
  5. Debe tener cuidado de NO realizar RPC (llamada a procedimiento remoto) desde el código de inicialización asincrónica (en InitializeAsync). Esto puede ocurrir cuando se llama GetService directa o indirectamente. Cuando se requieren cargas de sincronización, el subproceso de interfaz de usuario se bloqueará mediante JoinableTaskFactory. El modelo de bloqueo predeterminado deshabilita los RPC. Esto significa que si intenta usar un RPC desde las tareas asincrónicas, se interbloqueará si el subproceso de la interfaz de usuario está esperando a que se cargue el paquete. La alternativa general consiste en serializar el código en el subproceso de la interfaz de usuario si es necesario mediante algo como joinable Task Factory o SwitchToMainThreadAsync algún otro mecanismo que no use rpc. No use ThreadHelper.Generic.Invoke o bloquee generalmente el subproceso que realiza la llamada esperando llegar al subproceso de la interfaz de usuario.

    NOTA: Debe evitar el uso de GetService o QueryService en el InitializeAsync método . Si tiene que usarlos, primero deberá cambiar al subproceso de la interfaz de usuario. La alternativa es usar GetServiceAsync desde AsyncPackage (mediante su conversión a IAsyncServiceProvider).

    C#: Cree un 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 en AsyncPackage

La mayoría del trabajo es el mismo que crear un nuevo AsyncPackage. Siga los pasos del 1 al 5 anteriores. También debe tener precaución adicional con las siguientes recomendaciones:

  1. Recuerde quitar la invalidación que tenía en el Initialize paquete.

  2. Evitar interbloqueos: podría haber RPC ocultos en el código. que ahora sucede en un subproceso en segundo plano. Asegúrese de que si está realizando una RPC (por ejemplo, GetService), debe cambiar (1) al subproceso principal o (2) usar la versión asincrónica de la API si existe (por ejemplo, GetServiceAsync).

  3. No cambie entre subprocesos con demasiada frecuencia. Intente localizar el trabajo que puede producirse en un subproceso en segundo plano para reducir el tiempo de carga.

Consulta de servicios desde AsyncPackage

Un AsyncPackage puede cargar o no de forma asincrónica en función del autor de la llamada. Por ejemplo,

  • Si el autor de la llamada llamó a GetService o QueryService (ambas API sincrónicas) o

  • Si el autor de la llamada llamó a IVsShell::LoadPackage (o IVsShell5::LoadPackageWithContext) o

  • La carga se desencadena mediante un contexto de interfaz de usuario, pero no se ha especificado el mecanismo de contexto de la interfaz de usuario puede cargarse de forma asincrónica.

    a continuación, el paquete se cargará de forma sincrónica.

    El paquete todavía tiene una oportunidad (en su fase de inicialización asincrónica) para realizar el trabajo fuera del subproceso de la interfaz de usuario, aunque el subproceso de interfaz de usuario se bloqueará para que se complete ese trabajo. Si el autor de la llamada usa IAsyncServiceProvider para consultar de forma asincrónica el servicio, la carga y la inicialización se realizarán de forma asincrónica suponiendo que no se bloquean inmediatamente en el objeto de tarea resultante.

    C#: Procedimiento para consultar el servicio de forma asincrónica:

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;