У меня есть серверное приложение, которое получает информацию по сети и обрабатывает ее. Сервер является многопоточным и одновременно обрабатывает несколько сокетов, а потоки создаются без моего контроля с помощью методов стиля BeginInvoke и EndInvoke, которые связаны соответствующими функциями обратного вызова.
Я пытаюсь создать форму в дополнение к основному графическому интерфейсу пользователя, которая отображает элемент ListBox, заполненный элементами, описывающими подключенные в данный момент сокеты. Итак, в основном я пытаюсь добавить элемент в ListBox с помощью его функции Add () из потока, в котором выполняется соответствующая функция обратного вызова. Я получаю доступ к элементам управления формами через свойство Controls - I.E:
(ListBox)c.Controls["listBox1"].Items.Add();
Естественно, я не просто вызываю эту функцию, я пробовал несколько способов, которые нашел здесь и в Интернете, для связи между потоками, включая MethodInvoker, используя delegate, в сочетании с Invoke(), BeginInvoke() и т. д.
Кажется, ничего не работает, я всегда получаю одно и то же исключение, сообщающее мне, что к моему элементу управления обращались из потока, отличного от того, в котором он был создан.
Какие-нибудь мысли?





Использование BeginInvoke или Invoke должно работать нормально. Не могли бы вы опубликовать короткую, но полную программу, демонстрирующую проблему? Вы должны иметь возможность работать с одним, который на самом деле не требует никаких серверных вещей - просто создайте группу потоков, которые «притворяются», что принимают входящие соединения.
Я всегда использовал что-то в этом роде:
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);
}
Почему не использовать только один метод расширения, принимающий Action или MethodInvoker? Намного чище, и вызывающий всегда может заполнить остальное в делегате
Вы должны вызвать Invoke (или BeginInvoke) в элементе управления ListBox, к которому вы обращаетесь, чтобы делегат был вызван в потоке, создавшем этот элемент управления.
ListBox listBox = c.Controls["listBox1"] as ListBox;
if (listBox != null)
{
listBox.Invoke(...);
}
Одна важная тонкость заключается в том, что если вы читаете из элемента управления, вы обычно хотите использовать Invoke вместо BeginInvoke, чтобы убедиться, что переменная назначения в текущем потоке заполнена, прежде чем вы продолжите. Я не думаю, что у нас еще есть будущее. :)