Pregunta

Cuando usted se suscribe a un evento de un objeto dentro de un formulario, que son esencialmente la entrega del control de su método de devolución de llamada a la fuente del evento.Usted no tiene ninguna idea de si ese evento se seleccione para activar el evento en un subproceso diferente.

El problema es que cuando la devolución de llamada se invoca, usted no puede asumir que usted puede hacer la actualización de los controles en el formulario, ya que a veces esos controles se lanzará una excepción si el evento de devolución de llamada se llamó en un hilo diferente que el hilo el formulario se ejecuta.

¿Fue útil?

Solución

Para simplificar Simón código un poco, puede utilizar el construido en el genérico de la Acción de delegar.Ahorra acribillar su código con un montón de tipos de delegado que realmente no necesita.También, en .NET 3.5 se les ha añadido un parámetro params al método Invoke así que usted no tiene que definir una matriz temporal.

void SomethingHappened(object sender, EventArgs ea)
{
   if (InvokeRequired)
   {
      Invoke(new Action<object, EventArgs>(SomethingHappened), sender, ea);
      return;
   }

   textBox1.Text = "Something happened";
}

Otros consejos

Aquí están los puntos más destacados:

  1. Usted no puede hacer de interfaz de usuario de control de llamadas desde un subproceso diferente a la que fueron creados (la forma de la rosca).
  2. Delegado invocaciones (es decir, el evento ganchos) se activan en el mismo hilo que el objeto que está disparando el evento.

Por lo tanto, si usted tiene una "motor" hilo haciendo un trabajo y tiene una interfaz de usuario para ver los cambios de estado que pueden estar reflejados en la interfaz de usuario (tales como una barra de progreso o lo que sea), tienes un problema.El incendio en el motor de un objeto cambiado el evento que ha sido enganchado por la Forma.Pero el delegado de devolución de llamada que el Formulario registrado con el motor se llama en el motor del hilo... no en la Forma de la rosca.Y así no se puede actualizar cualquiera de los controles de callback.Doh!

BeginInvoke viene al rescate.Sólo tiene que utilizar esta simple modelo de codificación en todas sus métodos de devolución de llamada y usted puede estar seguro de que las cosas van a estar bien:

private delegate void EventArgsDelegate(object sender, EventArgs ea);

void SomethingHappened(object sender, EventArgs ea)
{
   //
   // Make sure this callback is on the correct thread
   //
   if (this.InvokeRequired)
   {
      this.Invoke(new EventArgsDelegate(SomethingHappened), new object[] { sender, ea });
      return;
   }

   //
   // Do something with the event such as update a control
   //
   textBox1.Text = "Something happened";
}

En realidad es bastante simple.

  1. Uso InvokeRequired para averiguar si esta devolución de llamada que sucedió en el hilo correcto.
  2. Si no, entonces reinvoke la devolución de llamada en el subproceso correcto con los mismos parámetros.Usted puede reinvoke un método mediante el Invocar (bloqueo) o BeginInvoke (sin bloqueo) métodos.
  3. La próxima vez que se llama a la función, InvokeRequired devuelve false porque ahora estamos en el hilo correcto y todo el mundo es feliz.

Este es un muy compacto, forma de abordar este problema y hacer que sus Formas seguro de multi-threaded evento devoluciones de llamada.

Yo uso de métodos anónimos mucho en este escenario:

void SomethingHappened(object sender, EventArgs ea)
{
   MethodInvoker del = delegate{ textBox1.Text = "Something happened"; }; 
   InvokeRequired ? Invoke( del ) : del(); 
}

Estoy un poco tarde a este tema, pero es posible que desee echar un vistazo a la Modelo Asincrónico Basado Evento-.Cuando se implementa correctamente, garantiza que los eventos siempre están planteadas desde el subproceso de interfaz de usuario.

He aquí un breve ejemplo que sólo permite que uno de los concurrentes invocación;soporte de múltiples invocaciones/eventos requiere de un poco más de plomería.

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public class MainForm : Form
    {
        private TypeWithAsync _type;

        [STAThread()]
        public static void Main()
        {
            Application.EnableVisualStyles();
            Application.Run(new MainForm());
        }

        public MainForm()
        {
            _type = new TypeWithAsync();
            _type.DoSomethingCompleted += DoSomethingCompleted;

            var panel = new FlowLayoutPanel() { Dock = DockStyle.Fill };

            var btn = new Button() { Text = "Synchronous" };
            btn.Click += SyncClick;
            panel.Controls.Add(btn);

            btn = new Button { Text = "Asynchronous" };
            btn.Click += AsyncClick;
            panel.Controls.Add(btn);

            Controls.Add(panel);
        }

        private void SyncClick(object sender, EventArgs e)
        {
            int value = _type.DoSomething();
            MessageBox.Show(string.Format("DoSomething() returned {0}.", value));
        }

        private void AsyncClick(object sender, EventArgs e)
        {
            _type.DoSomethingAsync();
        }

        private void DoSomethingCompleted(object sender, DoSomethingCompletedEventArgs e)
        {
            MessageBox.Show(string.Format("DoSomethingAsync() returned {0}.", e.Value));
        }
    }

    class TypeWithAsync
    {
        private AsyncOperation _operation;

        // synchronous version of method
        public int DoSomething()
        {
            Thread.Sleep(5000);
            return 27;
        }

        // async version of method
        public void DoSomethingAsync()
        {
            if (_operation != null)
            {
                throw new InvalidOperationException("An async operation is already running.");
            }

            _operation = AsyncOperationManager.CreateOperation(null);
            ThreadPool.QueueUserWorkItem(DoSomethingAsyncCore);
        }

        // wrapper used by async method to call sync version of method, matches WaitCallback so it
        // can be queued by the thread pool
        private void DoSomethingAsyncCore(object state)
        {
            int returnValue = DoSomething();
            var e = new DoSomethingCompletedEventArgs(returnValue);
            _operation.PostOperationCompleted(RaiseDoSomethingCompleted, e);
        }

        // wrapper used so async method can raise the event; matches SendOrPostCallback
        private void RaiseDoSomethingCompleted(object args)
        {
            OnDoSomethingCompleted((DoSomethingCompletedEventArgs)args);
        }

        private void OnDoSomethingCompleted(DoSomethingCompletedEventArgs e)
        {
            var handler = DoSomethingCompleted;

            if (handler != null) { handler(this, e); }
        }

        public EventHandler<DoSomethingCompletedEventArgs> DoSomethingCompleted;
    }

    public class DoSomethingCompletedEventArgs : EventArgs
    {
        private int _value;

        public DoSomethingCompletedEventArgs(int value)
            : base()
        {
            _value = value;
        }

        public int Value
        {
            get { return _value; }
        }
    }
}

Como el lazy programmer, Tengo muy perezoso método de hacer esto.

Lo que yo hago es simplemente esto.

private void DoInvoke(MethodInvoker del) {
    if (InvokeRequired) {
        Invoke(del);
    } else {
        del();
    }
}
//example of how to call it
private void tUpdateLabel(ToolStripStatusLabel lbl, String val) {
    DoInvoke(delegate { lbl.Text = val; });
}

Usted podría poner en línea el DoInvoke en el interior de su función o se esconden dentro de separar la función de hacer el trabajo sucio por usted.

Sólo ten en cuenta que puede pasar funciones directamente en la DoInvoke método.

private void directPass() {
    DoInvoke(this.directInvoke);
}
private void directInvoke() {
    textLabel.Text = "Directly passed.";
}

En muchos casos simples, usted puede utilizar el MethodInvoker delegado y evitar la necesidad de crear su propio tipo de delegado.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top