Многопоточные делегаты / События - PullRequest
2 голосов
/ 06 мая 2010

Я пытаюсь отключить части пользовательского интерфейса в приложении .NET на основе опроса, выполненного в фоновом потоке. Фоновый поток проверяет, является ли соединение с глобальной базой данных, используемое приложением, все еще открытым и работоспособным.

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

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

Очевидно, что есть правильный способ сделать это, но у нас ограниченный опыт работы с событиями (в основном однопоточными приложениями), и почти ни один с делегатами. Я прочитал документацию и примеры для делегатов, и кажется, что это ближе к тому, что нам нужно, но я не уверен, как заставить это работать в этом случае.

Приложение написано в основном на VB.NET, но пример или помощь на C # тоже подойдут.

Ответы [ 3 ]

2 голосов
/ 06 мая 2010

Вы можете маршалировать подъем события обратно в поток пользовательского интерфейса. Поскольку вероятно (если вы хорошо спроектировали) это делается из отдельного класса, я рекомендую передать копию текущего SynchronizationContext в ваш класс, который обрабатывает фоновый процесс.

Вот пример кода на C #:

public class BackgroundWork
{
    public SynchronizationContext Context { get; private set; }
    public BackgroundWork(SynchronizationContext context)
    {
        this.Context = context;
    }

    // Thread handler, etc...

    // Method to raise event
    void RaiseEvent()
    {
         Context.Post( (state) =>
         {
             // Raise the event
             this.ConnectionStatusChanged(this, EventArgs.Empty);
         }, null);
    }
}

Затем, когда вы создаете класс (в потоке пользовательского интерфейса), передайте ему текущий контекст:

BackgroundWork worker = new BackgroundWork(SynchronizationContext.Current); // This passes the UI thread context...
worker.Start();

(Кстати, это та же самая техника, которую использует класс BackgroundWorker , поэтому он работает одинаково как для Windows Forms, так и для WPF ...)

0 голосов
/ 06 мая 2010

Попробуйте Invoke метод элемента управления, который вы хотите изменить, или любой другой элемент управления и предоставьте небольшую функцию для выполнения желаемой работы. Или см. BeginInvoke для асинхронного выполнения.

Просто будьте осторожны с проблемами потоков, изменения переменных, которые изменяет "основной" поток, и т. Д.

0 голосов
/ 06 мая 2010

Вы решаете ошибку выполнения кросс-потока, используя Control.Dispatcher.Invoke:

public void EnventHandlerMethod(object sender, EventArgs e)
{
    myLabel.Dispatch.Invoke(new Action(() => { myLabel.Content = "Updated"; }));
}

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

Также это (я думаю) предполагает .NET 3.5

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...