Обработка событий с помощью кросс-потоков в С#

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

class SomeDataSource {

    public event OnFrameworkEvent;

    void FrameworkCallback() {

        // This function runs on framework thread.

        if (OnFrameworkEvent != null)
            OnFrameworkEvent(args);
    }
}

Я хочу передать эти события объекту Winforms в поток Winforms. Я, очевидно, проверяю InvokeRequired и отправляю его в поток Winforms, если это необходимо.

class SomeForm : Form {

    // ...

    public void SomeAction(SomeArgs args) {
        if (InvokeRequired) {
            BeginInvoke(new Action(SomeAction), args);
            return;
        }

        // ...
    }

}

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

var form = new SomeForm();
var src = new SomeDataSource();

// ...

src.OnFrameworkEvent += form.SomeAction;
form.Closing += (sender, eargs) => src.OnFrameworkEvent -= form.SomeAction;
  • Теперь, этот подход небезопасен? Если форма находится в процессе закрытия, а внешний поток вызывает BeginInvoke, будет ли вызов запущен для выполнения, если форма закрыта? (что означает, что у меня все еще есть возможность столкнуться с той же проблемой)

  • Существует ли более эффективный подход или рекомендуемый шаблон для обработки событий с несколькими потоками?

Ответ 1

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

Посмотрите этот поток для получения дополнительной информации.

Ответ 2

Вы можете добавить этот код в конструктор CheckForIllegalCrossThreadCalls = false;, и никакое исключение не будет выбрано.

Ответ 3

Я не использовал фреймворк со своим диспетчером событий, но у меня был собственный опыт с потоками, которые я создал. Вот мой опыт

  • Этот подход не является потокобезопасным. Вызов будет вызываться, даже если сама программа закрыта. Я видел это в диспетчере задач (после того, как программа закрыта, как вы говорите), как висящие потоки. (даже если вы также запустите программу из диспетчера задач). Я должен был убить эти потоки отдельно позже.

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

    form.Closing += (sender, eargs) => src.OnFrameworkEvent -= form.SomeAction;
     // pseudo-code (find c# equivalent)
    if (dispatcherthread.isrunning)
    dispatcherThread.kill();