¿Cómo proteger los datos de otro hilo para usarlos en los controles?

Tengo una biblioteca que sirve usando datos de eventos provenientes de operaciones tcp asíncronas. Cuando utilizo esos datos en los controles después de que se recibieron en la interfaz de usuario, aunque obtengo la excepción Cross-Thread Opration. Cómo resolver este problema antes de que el consumidor de la biblioteca obtenga los datos para mostrar en sus controles. Entonces, básicamente, necesito lanzar los datos a su propio hilo donde usa la biblioteca.

El mismo código utilizado para el marco compacto con archivos vinculados.

Estoy usando dentro de la biblioteca este método con un Control de ayuda para decir si se requiere invocar pero no funciona.

public static void InvokeIfNecessary(Control control, Action setValue)
{

    if (control.InvokeRequired)
    {
        control.Invoke(setValue);
    }
    else
    {
        setValue();
    }
}

Un código de muestra que usa un evento para entregar datos al usuario que usa la biblioteca.

    if (OnClientChangeConnection != null) SafeData.InvokeIfNecessary(_helpControl, () => OnClientChangeConnection(ConnectedClients, requestClientInfo)); // ConnectedClients is an integer and requestClientInfo is a List<ClientInfo> class type.

Gracias por su atención.

preguntado el 16 de mayo de 11 a las 18:05

¿Por qué no funciona? ¿Lo que pasa? -

No lo entiendo también, simplemente nunca tiene el control. Invoque (setValue) if. La biblioteca se utiliza para fines de comunicación Async TCP. Strange está en Compact Framework, no tengo ningún problema. -

¿Qué ves en el depurador? -

Solo veo que Invoke no es necesario. ¿A qué te refieres con que debería estar atento? -

Entonces ya estás en el hilo de la interfaz de usuario, probablemente debido a un SyncContext. (O eso o el control aún no existe) -

2 Respuestas

Puede guardar una referencia a SynchronizationContext.Current desde el hilo de la interfaz de usuario, luego llame a Post or Send métodos para ejecutar código en el hilo de la interfaz de usuario.

contestado el 17 de mayo de 11 a las 01:05

La forma correcta de hacer esto es usar el objeto SynchronizationContext. He incluido código de muestra. Básicamente, lo que tiene que hacer es envolver su tarea de subproceso en una clase que puede guardar una referencia a un objeto de contexto de sincronización y una devolución de llamada proporcionada por el subproceso principal y luego los llama después de que el trabajo está inactivo.

Esta es una forma simple:

public partial class Form1 : Form
{
    private SynchronizationContext _synchronizationContext;

    public Form1()
    {
        InitializeComponent();
        //Client must be careful to create sync context soimehwere they are sure to be on main thread
        _synchronizationContext = AsyncOperationManager.SynchronizationContext;
    }

    //Callback method implementation - must be of this form
    public void ReceiveThreadData(object threadData)
    {
        // Can use directly in UI without error
        this.listBoxMain.Items.Add((string)threadData);
    }

    private void DoSomeThreadWork()
    {
        // Thread needs callback and sync context.  
        // You probably want to derive your own callback from the NET SendOrPostCallback class.
        SendOrPostCallback callback = new SendOrPostCallback(ReceiveThreadData);
        SomeThreadTask task = new SomeThreadTask(_synchronizationContext, callback);
        Thread thread = new Thread(task.ExecuteThreadTask);
        thread.Start();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        DoSomeThreadWork();
    }

}

Esta es una clase que tiene la tarea de hilo

/// SomeThreadTask defines the work a thread needs to do and also provides any data required along with callback pointers etc.
/// Populate a new SomeThreadTask instance with a synch context and callnbackl along with any data the thread needs
/// then start the thread to execute the task.
/// </summary>
public class SomeThreadTask
{

    private string _taskId;
    private SendOrPostCallback _completedCallback;
    private SynchronizationContext _synchronizationContext;

    /// <summary>
    /// Get instance of a delegate used to notify the main thread when done.
    /// </summary>
    internal SendOrPostCallback CompletedCallback
    {
        get { return _completedCallback; }
    }

    /// <summary>
    /// Get SynchronizationContext for main thread.
    /// </summary>
    internal SynchronizationContext SynchronizationContext
    {
        get { return _synchronizationContext; }
    }

    /// <summary>
    /// Thread entry point function.
    /// </summary>
    public void ExecuteThreadTask()
    {

        //Just sleep instead of doing any real work
        Thread.Sleep(5000);

        string message = "This is some spoof data from thread work.";

        // Execute callback on synch context to tell main thread this task is done.
        SynchronizationContext.Post(CompletedCallback, (object)message);


    }

    public SomeThreadTask(SynchronizationContext synchronizationContext, SendOrPostCallback callback)
    {
        _synchronizationContext = synchronizationContext;
        _completedCallback = callback;
    }

}

contestado el 17 de mayo de 11 a las 02:05

Gracias por su tiempo, esta parece una buena solución, marcaré como respuesta la primera y le daré un punto para que de alguna manera comparta algo de crédito. Que tú - George Taskos

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.