Как запустить событие, не дожидаясь запуска обработчиков событий?

У меня вопрос о событиях в .NET (C#). Мне приходилось писать код для нескольких ситуаций, когда у меня запущена фоновая задача, и я хочу уведомить основной поток или класс контроллера о том, что что-то произошло, например, задача завершена или завершено копирование файла, но я этого не делал. Не нужно, чтобы фоновая задача ждала, пока делегат основного потока обработает событие.

Я хочу сделать что-то вроде передачи сообщений: отправить сообщение, но кого волнует, что они с ним сделают.

Например:

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

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

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

Пример 2: программа резервного копирования.

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


person Chris Thompson    schedule 21.05.2009    source источник


Ответы (4)


Вы можете выполнить асинхронный вызов при вызове события (как уже упоминалось) или просто вызвать само событие в фоновом потоке:

void OnUpdated(EventArgs e) {
   EventHandler h = this.Updated;
   if (h != null) h(e);
}

void DoStuff() {
   BigMethod();
   ThreadPool.QueueUserWorkItem(OnUpdated, EventArgs.Empty);
   BigMethod2();
}

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

Вызов события в фоновом потоке содержит те же предостережения для методов вашего класса, но вам не нужно беспокоиться о самом классе EventArgs.

person Mark Brackett    schedule 21.05.2009

Похоже, вы пытаетесь асинхронно вызвать делегатов в списке вызовов события.

Я бы посоветовал вам прочитать Асинхронные события .NET для отправки статуса процесса в пользовательский интерфейс:

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

person Andrew Hare    schedule 21.05.2009
comment
Я читал о шаблоне BeginInvoke/EndInvoke, но думал об использовании его на стороне слушателя, а не на стороне броска. Может ли быть проблема, если есть тонны невыполненных вызовов BeginInvoke? - person Chris Thompson; 21.05.2009

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

person Luke Schafer    schedule 21.05.2009
comment
Ну, кажется, ты знаешь, как начать еще одну тему. Просто сделайте так, чтобы первый прослушиватель событий сделал это (как только вы создадите объект с событием, назначьте обработчик - person Luke Schafer; 22.05.2009

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

Вы можете использовать асинхронные делегаты

public class AsyncFileCopier
    {
        public delegate void FileCopyDelegate(string sourceFile, string destFile);

        public static void AsynFileCopy(string sourceFile, string destFile)
        {
            FileCopyDelegate del = new FileCopyDelegate(FileCopy);
            IAsyncResult result = del.BeginInvoke(sourceFile, destFile, CallBackAfterFileCopied, null);
        }

        public static void FileCopy(string sourceFile, string destFile)
        { 
            // Code to copy the file
        }

        public static void CallBackAfterFileCopied(IAsyncResult result)
        {
            // Notify UI by calling an async del (probably using fire & forget approach or another callback if desired)
        }
    }

Вы можете назвать это как:

AsyncFileCopier.AsynFileCopy("abc.txt", "xyz.txt");

Эта ссылка рассказывает о различных методах асинхронного кодирования.

person SO User    schedule 21.05.2009