Нужна помощь в получении информации по потоку пользовательского интерфейса и другому потоку в C #

У меня есть серверное приложение, которое получает информацию по сети и обрабатывает ее. Сервер является многопоточным и одновременно обрабатывает несколько сокетов, а потоки создаются без моего контроля с помощью методов стиля BeginInvoke и EndInvoke, которые связаны соответствующими функциями обратного вызова.

Я пытаюсь создать форму в дополнение к основному графическому интерфейсу пользователя, которая отображает элемент ListBox, заполненный элементами, описывающими подключенные в данный момент сокеты. Итак, то, что я в основном пытаюсь сделать, - это добавить элемент в ListBox с помощью его функции Add () из потока, в котором выполняется соответствующая функция обратного вызова. Я получаю доступ к элементам управления формами через свойство Controls - I.E:

(ListBox)c.Controls["listBox1"].Items.Add();

Естественно, я не просто вызываю функцию, я пробовал несколько способов, которые нашел здесь и в Интернете, для связи между потоками, включая MethodInvoker, используя delegate в сочетании с Invoke(), BeginInvoke() и т. Д. Кажется, ничего не работает, Я всегда получаю одно и то же исключение, сообщающее мне, что к моему элементу управления обращались из потока, отличного от того, в котором он был создан.

Есть предположения?


person Community    schedule 23.10.2008    source источник
comment
Одна важная особенность заключается в том, что при чтении из элемента управления обычно требуется использовать Invoke вместо BeginInvoke, чтобы обеспечить заполнение целевой переменной в текущем потоке перед продолжением. Я не думаю, что у нас еще есть будущее. :)   -  person Greg D    schedule 23.10.2008


Ответы (3)


Вы должны вызвать Invoke (или BeginInvoke) в элементе управления ListBox, к которому вы обращаетесь, чтобы делегат был вызван в потоке, создавшем этот элемент управления.

ListBox listBox = c.Controls["listBox1"] as ListBox;
if(listBox != null)
{
   listBox.Invoke(...);
}
person Magnus Lindhe    schedule 23.10.2008

Я всегда использовал что-то в этом роде:

        c = <your control>
        if (c.InvokeRequired)
        {
            c.BeginInvoke((MethodInvoker)delegate
            {
                //do something with c
            });
        }
        else
        {
            //do something with c
        }

Я также написал кучу вспомогательных методов расширения, чтобы ... помочь.

using System;
using System.ComponentModel;
public static class CrossThreadHelper
{
    public static bool CrossThread<T,R>(this ISynchronizeInvoke value, Action<T, R> action, T sender, R e)
    {
        if (value.InvokeRequired)
        {
            value.BeginInvoke(action, new object[] { sender, e });
        }

        return value.InvokeRequired;
    }
}

используется так:

     private void OnServerMessageReceived(object sender, ClientStateArgs e)
    {
        if (this.CrossThread((se, ev) => OnServerMessageReceived(se, ev), sender, e)) return;
        this.statusTextBox.Text += string.Format("Message Received From: {0}\r\n", e.ClientState);
    }
person Hath    schedule 23.10.2008
comment
Почему не использовать только один метод расширения, принимающий Action или MethodInvoker? Намного чище, и вызывающий всегда может заполнить остальное в делегате - person Marc Gravell; 23.10.2008

Использование BeginInvoke или Invoke должно работать нормально. Не могли бы вы опубликовать короткую, но полную программу, демонстрирующую проблему? Вы должны иметь возможность работать с одним, который на самом деле не требует никаких серверных вещей - просто создайте группу потоков, которые «притворяются», что принимают входящие соединения.

person Jon Skeet    schedule 23.10.2008