2010-03-18 9 views
6

He heredado el código donde se invoca BeginInvoke desde el hilo principal (no un hilo de fondo, que suele ser el patrón). Estoy tratando de entender lo que realmente hace en este escenario.Implicaciones de rendimiento de BeginInvoke

¿El método al que se llama en el BeginInvoke entra en la línea de mensajes que bajan a la ventana? Los documentos dicen asynchronously, así que esa es mi suposición.

¿Cómo prioriza la estructura cuándo iniciar el método llamado por BeginInvoke?

Editar: El código es el siguiente:

System.Action<bool> finalizeUI = delegate(bool open) 
{ 
    try 
    { 
     // do somewhat time consuming stuff 
    } 
    finally 
    { 
     Cursor.Current = Cursors.Default; 
    } 
}; 

Cursor.Current = Cursors.WaitCursor; 
BeginInvoke(finalizeUI, true); 

Esto está sucediendo en el evento Form_Load.

Respuesta

4

edición

Ahora que vemos el código, está claro que esto es sólo una manera de mover alguna inicialización de Form_Load pero todavía tienen que suceder antes de que el usuario puede interactuar con el formulario.

La llamada a BeginInvoke está dentro de Form_load, y no se invoca a otro objeto, por lo que se trata de una llamada a Form.BeginInvoke. Entonces, ¿qué está pasando es esto?

  1. Form_Load pasa a un delegado a Form.BeginInvoke, esto pone un mensaje en la cola de mensajes de la forma que es por delante de todos los mensajes de entrada de usuario. Establece el cursor en un cursor de espera.
  2. Devuelve Form_Load, y el resto de la inicialización del formulario puede completarse, la forma más probable es que se vuelva visible en este punto.
  3. Una vez que el código cae en la bomba de mensajes, lo primero que se ve en la cola es el delegado, por lo que ejecuta eso.
  4. como el delegado completa, cambia el cursor al cursor normal, y devuelve
  5. profit!

post original a continuación


que depende del objeto que llame BeginInvoke sucesivamente. Si el objeto se deriva de Control, entonces Control.BeginInvoke se ejecutará en la cadena que creó el control. Ver la respuesta de JaredPar.

Pero hay otro patrón para el uso de BeginInvoke. si el objeto es un delegado, BeginInvoke ejecuta la devolución de llamada en un hilo separado, uno que se puede crear específicamente para ese propósito.

public class Foo 
{ 
    ... 
    public Object Bar(object arg) 
    { 
     // this function will run on a separate thread. 
    } 
} 

... 

// this delegate is used to Invoke Bar on Foo in separate thread, this must 
// take the same arguments and return the same value as the Bar method of Foo 
public delegate object FooBarCaller (object arg); 

... 

// call this on the main thread to invoke Foo.Bar on a background thread 
// 
public IAsyncResult BeginFooBar(AsyncCallback callback, object arg) 
{ 
    Foo foo = new Foo(); 
    FooBarCaller caller = new FooBarCaller (foo.Bar); 
    return caller.BeginInvoke (arg); 
} 

Este patrón es uno de los motivos por los que se llama a BeginInvoke desde el hilo principal y no desde un hilo de fondo.

+0

@ John Knoeller, ¿estás diciendo que, en mi escenario, BeginInvoke tiene el peso del cambio de contexto del hilo? – AngryHacker

+0

@AngryHacker: No, ahora que ha mostrado su código, está claro que esto es solo un PostMessage como JaredPar, su BeginInvoke es un método en el formulario, y por lo tanto _no_ ejecutará el delegado en un hilo separado. –

2

En el caso de que se invoque a BeginInvoke en un subproceso de interfaz de usuario, pasará por el proceso de publicar un mensaje de Windows en la cola de mensajes donde el mensaje esperará a procesarse. El delegado se ejecutará cuando se procese el mensaje. Este mensaje no tiene prioridad de ninguna manera que sea diferente de lo que se llama desde el hilo de fondo.

+1

No espera. –

+0

@nobugz, terrible redacción de mi parte. Intenté decir que el mensaje en sí se colocará en la cola donde espera que se procese, no porque la persona que llama espera. Limpió el idioma. – JaredPar

+0

¿Está seguro de esto? BeginInvoke es un PostMessage en lugar de un SendMessage o SendNotifyMessage? –

1

En este escenario sospecho que la llamada se ve así:

private void Button1_Click(object sender, ButtonClickEventArgs e) 
{ 
    Control.BeginInvoke(new MethodInvoker(()=> /* code etc. */)); 
} 

Lo que pasa es que algún código se ejecutará en un hilo de subprocesos, y actualizar el control en el subproceso que crea el control mientras que si Se usó Control.Invoke, , algún código se ejecutaría en el subproceso que creó el control y también actualizará el control en ese subproceso.

1

Antes del uso generalizado de BackgroundWorker, tenía que sincronizar de nuevo con el subproceso de la interfaz de usuario antes de realizar cualquier operación en los controles creados en el subproceso de la interfaz de usuario (es decir, casi todos los controles).

Hay un buen ejemplo de referencia here en el "Llamadas a prueba de subprocesos a un control de Windows Forms" sección.

Cuestiones relacionadas