Как сохранить данные из другого потока для использования на элементах управления? - PullRequest
0 голосов
/ 16 мая 2011

У меня есть библиотека, которая обслуживает данные о событиях, поступивших из асинхронных операций TCP. При использовании этих данных в элементах управления после их получения в пользовательском интерфейсе я получаю исключение Cross-Thread Opration. Как решить эту проблему, прежде чем пользователь библиотеки получит данные для отображения на своих элементах управления. Так что в основном мне нужно выбросить данные в свой собственный поток, где используется библиотека?

Тот же код, который используется для компактной среды со связанными файлами.

Я использую внутри библиотеки этот метод с помощью Control, чтобы сказать, требуется ли вызов, но он не работает.

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

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

Пример кода, использующего событие для передачи данных пользователю, использующему библиотеку.

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

Спасибо.

Ответы [ 2 ]

1 голос
/ 17 мая 2011

Правильный способ сделать это - использовать объект SynchronizationContext. Я включил пример кода. По сути, вам нужно обернуть задачу потока в класс, который может сохранить ссылку на объект контекста синхронизации и обратный вызов, предоставленный основным потоком, а затем вызвать их после завершения работы.

Это простая форма:

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();
    }

}

Это класс, который имеет задачу потока

/// 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;
    }

}
0 голосов
/ 17 мая 2011

Вы можете сохранить ссылку на SynchronizationContext.Current из потока пользовательского интерфейса, а затем вызвать ее методы Post или Send для запуска кода в потоке пользовательского интерфейса.

...