VB.NET Два разных подхода к универсальным многопоточным операциям; что лучше? - PullRequest
5 голосов
/ 15 февраля 2011

VB.NET 2010, .NET 4

Здравствуйте,

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

Обновление: Этот вопрос частично мотивирован такими утверждениями, как следующие со страницы MSDN на Control.InvokeRequired.

Еще лучшим решением является использование SynchronizationContext возвращено SynchronizationContext, а не управление маршалингом с поперечной резьбой.

А также общее заблуждение относительно того, почему, когда я оглядываюсь вокруг, большинство ответов на вопросы, касающиеся проблемы такого типа в SO, предлагают подход Invoke без упоминания этого метода.

Метод 1:

Public Sub InvokeControl(Of T As Control)(ByVal Control As T, ByVal Action As Action(Of T))
    If Control.InvokeRequired Then
        Control.Invoke(New Action(Of T, Action(Of T))(AddressOf InvokeControl), New Object() {Control, Action})
    Else
        Action(Control)
    End If
End Sub

Метод 2:

Public Sub UIAction(Of T As Control)(ByVal Control As T, ByVal Action As Action(Of Control))
    SyncContext.Send(New Threading.SendOrPostCallback(Sub() Action(Control)), Nothing)
End Sub

Где SyncContext - это Threading.SynchronizationContext объект, определенный в конструкторе моей формы пользовательского интерфейса (я храню его в модуле ... Не уверен, что это лучший выбор):

Public Sub New()
    InitializeComponent()
    SyncContext = WindowsFormsSynchronizationContext.Current
End Sub

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

InvokeControl(Label1, Sub(x) x.Text = "hello")

или

UIAction(Label1, Sub(x) x.Text = "hello")

Итак, что вы думаете? Является ли один способ предпочтительным или зависит от контекста? Если у вас есть время, мы будем благодарны за многословие!

Заранее спасибо,
Brian

1 Ответ

4 голосов
/ 16 февраля 2011

Ну, я немного читаю и, поскольку я не получаю никаких ответов, я решил начать частичный ответ на свой вопрос, содержащий то, что я нашел до сих пор:

Я нашел интересную статью codeproject , в которой обсуждается использование SynchronizationContext для маршалинга кода между потоками (и в частности из рабочих потоков в поток пользовательского интерфейса).Некоторые наблюдения, которые мне показались интересными:

  • Объект SynchronizationContext потока пользовательского интерфейса создается при создании первого элемента управления в этом потоке.До этого он не определен.
  • SynchronizationContext для потока пользовательского интерфейса - это не экземпляр класса SynchronizationContext, а класс System.Windows.Forms.WindowsFormsSynchronizationContext, производный от SynchronizationContext.Именно этот класс определяет поведение Post / Send, позволяющее маршалинг кода из одного потока в другой.
  • Привлекательность передачи SynchronizationContext потока пользовательского интерфейса вместо использования Invokeчто вам не нужно хранить ссылку на форму UI в логике, чтобы вызвать ее.
  • Метод Post кажется привлекательным для выполнения таких операций, как обновление индикатора, поскольку он не блокирует, а, посколькуВ статье указывается, что исключения, добавленные в размещенный код, добавляются в поток пользовательского интерфейса.т. е. ошибка в коде, размещенном в пользовательском интерфейсе, может привести к сбою пользовательского интерфейса.Send не имеет этой проблемы.Исключения, возникающие при отправке, добавляются в рабочий поток.

Обновление: вот еще одна проницательная статья .В этой статье Кель Роуэн обсуждает контекст, в котором использование SynchronizationContext может быть предпочтительнее методов Invoke / BeginInvoke экземпляра элемента управления.Он утверждает, что при написании библиотеки многократного использования нежелательно поддерживать ссылку на элемент управления вне библиотеки просто для целей вызова.Он предоставляет код для делегата, который гарантирует, что любой новый созданный поток будет иметь доступ к потоку пользовательского интерфейса SynchronizationContext.

Хорошо, похоже, я не собираюсь больше получать здесь комментарии.То, что я написал здесь, настолько близко, насколько мое невежество позволяет мне найти ответ.Если кому-то еще есть что добавить, я бы, конечно, это оценил, но сейчас я ухожу.: /

...