Как я делаю eventhandler выполненным асинхронно?

Я пишу Визуальную программу C#, которая выполняет непрерывный цикл операций на вторичном потоке. Иногда, когда тот поток заканчивает задачу, я хочу, чтобы он инициировал eventhandler. Моя программа делает это, но, когда обработчик событий инициирован, вторичный поток ожидает, пока обработчик событий не закончен прежде, чем продолжить поток. Как я заставляю его продолжиться? Вот способ, которым мне в настоящее время структурировали его...

class TestClass 
{
  private Thread SecondaryThread;
  public event EventHandler OperationFinished;

  public void StartMethod()
  {
    ...
    SecondaryThread.Start();      //start the secondary thread
  }

  private void SecondaryThreadMethod()
  {
    ...
    OperationFinished(null, new EventArgs());
    ...  //This is where the program waits for whatever operations take
         //place when OperationFinished is triggered.
  }

}

Этот код является частью API для одного из моих устройств. Когда событие OperationFinished инициировано, я хочу, чтобы клиентское приложение было в состоянии сделать независимо от того, что ему нужно к (т.е. обновите GUI соответственно) без haulting операция API.

Кроме того, если я не хочу передавать какие-либо параметры обработчику событий, мой синтаксис, корректный при помощи OperationFinished(null, new EventArgs()) ?

41
задан PICyourBrain 16 December 2009 в 17:03
поделиться

5 ответов

Итак, вы хотите вызвать событие таким образом, чтобы не дать слушателям блокировать фоновый поток? Дайте мне пару минут, чтобы привести пример; это довольно просто: -)

Итак: сначала важное замечание! Всякий раз, когда вы вызываете BeginInvoke , вы должны вызывать соответствующий EndInvoke , иначе, если вызванный метод вызвал исключение или вернул значение, тогда поток ThreadPool никогда не будет выпущен обратно в пул, что приведет к утечке потока!

class TestHarness
{

    static void Main(string[] args)
    {
        var raiser = new SomeClass();

        // Emulate some event listeners
        raiser.SomeEvent += (sender, e) => { Console.WriteLine("   Received event"); };
        raiser.SomeEvent += (sender, e) =>
        {
            // Bad listener!
            Console.WriteLine("   Blocking event");
            System.Threading.Thread.Sleep(5000);
            Console.WriteLine("   Finished blocking event");
        };

        // Listener who throws an exception
        raiser.SomeEvent += (sender, e) =>
        {
            Console.WriteLine("   Received event, time to die!");
            throw new Exception();
        };

        // Raise the event, see the effects
        raiser.DoSomething();

        Console.ReadLine();
    }
}

class SomeClass
{
    public event EventHandler SomeEvent;

    public void DoSomething()
    {
        OnSomeEvent();
    }

    private void OnSomeEvent()
    {
        if (SomeEvent != null)
        {
            var eventListeners = SomeEvent.GetInvocationList();

            Console.WriteLine("Raising Event");
            for (int index = 0; index < eventListeners.Count(); index++)
            {
                var methodToInvoke = (EventHandler)eventListeners[index];
                methodToInvoke.BeginInvoke(this, EventArgs.Empty, EndAsyncEvent, null);
            }
            Console.WriteLine("Done Raising Event");
        }
    }

    private void EndAsyncEvent(IAsyncResult iar)
    {
        var ar = (System.Runtime.Remoting.Messaging.AsyncResult)iar;
        var invokedMethod = (EventHandler)ar.AsyncDelegate;

        try
        {
            invokedMethod.EndInvoke(iar);
        }
        catch
        {
            // Handle any exceptions that were thrown by the invoked method
            Console.WriteLine("An event listener went kaboom!");
        }
    }
}
58
ответ дан 27 November 2019 в 00:25
поделиться

Кроме того, если я не хочу передавать какие-либо параметры обработчику событий, верен ли мой синтаксис с использованием OperationFinished (null, new EventArgs ())?

Нет. Обычно вы называете это так:

OperationFinished(this, EventArgs.Empty);

Вы всегда должны передавать объект в качестве отправителя - это ожидается в шаблоне (хотя обычно игнорируется). EventArgs.Empty также лучше, чем new EventArgs ().

Чтобы запустить это в отдельном потоке, самым простым вариантом, вероятно, будет просто использовать пул потоков:

private void RaiseOperationFinished()
{
       ThreadPool.QueueUserWorkItem( new WaitCallback( (s) =>
           {
              if (this.OperationFinished != null)
                   this.OperationFinished(this, EventArgs.Empty);
           }));
}

При этом возникает событие отдельный поток - это то, что следует тщательно задокументировать, поскольку это потенциально может вызвать неожиданное поведение.

12
ответ дан 27 November 2019 в 00:25
поделиться

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

6
ответ дан 27 November 2019 в 00:25
поделиться

I prefer to define a method that I pass to the child thread as a delegate which updates the UI. First define a delegate:

public delegate void ChildCallBackDelegate();

In the child thread define a delegate member:

public ChildCallbackDelegate ChildCallback {get; set;}

In the calling class define the method that updates the UI. You'll need to wrap it in the target control's dispatcher since its being called from a separate thread. Note the BeginInvoke. In this context EndInvoke isn't required:

private void ChildThreadUpdater()
{
  yourControl.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background
    , new System.Threading.ThreadStart(delegate
      {
        // update your control here
      }
    ));
}

Before you launch your child thread, set its ChildCallBack property:

theChild.ChildCallBack = new ChildCallbackDelegate(ChildThreadUpdater);

Then when the child thread wants to update the parent:

ChildCallBack();
0
ответ дан 27 November 2019 в 00:25
поделиться

Посмотрите на класс BackgroundWorker . Я думаю, он делает именно то, о чем вы просите.

РЕДАКТИРОВАТЬ: Я думаю, вы спрашиваете, как запустить событие, когда выполнена только небольшая часть общей фоновой задачи. BackgroundWorker предоставляет событие под названием «ProgressChanged», которое позволяет вам сообщить основному потоку, что некоторая часть всего процесса завершена. Затем, когда вся асинхронная работа завершена, вызывается событие «RunWorkerCompleted».

0
ответ дан 27 November 2019 в 00:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: