Комментарии 17
Универсальный велосипед
public static class Execute
{
private static Action<Action> executor = action => action();
public static void InitializeWithSynchronizationContext()
{
var context = SynchronizationContext.Current;
if (context == null)
{
throw new InvalidOperationException("Unable to initialize synchronization context");
}
executor = action =>
{
if (Equals(context, SynchronizationContext.Current))
{
action();
}
else
{
SendAction(context, action);
}
};
}
public static void OnUIThread(this Action action)
{
executor(action);
}
private static void SendAction(SynchronizationContext context, Action action)
{
ExceptionDispatchInfo exceptionDispatchInfo = null;
context.Send(state =>
{
try
{
action();
}
catch (Exception ex)
{
exceptionDispatchInfo = ExceptionDispatchInfo.Capture(ex);
}
}, null);
if (exceptionDispatchInfo != null)
{
exceptionDispatchInfo.Throw();
}
}
}
+4
В рамках одного потока это работает – сообщения приходят, форма их получает, данные обновляются. При добавлении же еще нескольких, наша функция SendNotificationToForm начинает вести не совсем так, как ожидалось. Сообщения приходят и форма их получат, вот только складывает их в очередь для обновления контента и пока ваши background потоки не закончат работу, не спешит выводить на экран. И чем более сложной задачей заняты вы в других потоках, тем нагляднее это проявляется.Не верю — никогда не сталкивался с подобным. Сообщения всегда приходили вовремя, сколько бы потоков я не создавал.
Однако, контексты синхронизации следует использовать хотя бы потому что их время жизни больше, чем время жизни формы (а время жизни дескриптора окна — меньше). С контекстами синхронизации вы никогда не столкнетесь с вылетом программы при закрытии окна, да и начинать операцию при открытии окна можно прямо из конструктора, а не из события HandleCreated.
+1
К сожалению, пример собран на реальном коде. Несколько потоков, порожденных друг из друга и подписка на форме, где идет анализ callbacks и вывод полученных данных. По логам — данные до формы приходят штатно, а с визуализацией через BeginInvoke — проблема. Форма аккуратно складывает информацию у себя, затем ждет окончание работы потока и лишь после этого выводит накопленное.
При работе с контекстом — такого не происходит.
Я смотрел статьи на эту тему, народ предлагает для подобных случаев использовать именно SynchronizationContext.
PS. Повторюсь еще раз — речь именно про WinForm, не WPF.
При работе с контекстом — такого не происходит.
Я смотрел статьи на эту тему, народ предлагает для подобных случаев использовать именно SynchronizationContext.
PS. Повторюсь еще раз — речь именно про WinForm, не WPF.
0
Повторюсь еще раз — проблема у вас в коде. Форма никого не ждет, ибо не умеет.
Вы там случайно Invoke вместо BeginInvoke ни разу не написали? Или синхронное действие в потоку UI не выполнили?
Вы там случайно Invoke вместо BeginInvoke ни разу не написали? Или синхронное действие в потоку UI не выполнили?
0
Нет, не выполнял.
3 потока, порожденных из основного «цепочкой», затем форма стоит на ShowDialog. Потоки при этом заняты своими делами и кидают сообщения. Первый поток — сообщения от него форма выводит без задержек, остальные потоки — сообщения на форму приходят, выполняется BeginInvoke, после чего — вывода данных нет до момента окончания потока. Все callback с потоков оформлены как асинхронные.
Переписал посылку данных с callback через SynchronizationContext вместо BeginInvoke — все стало отрисовывать.
3 потока, порожденных из основного «цепочкой», затем форма стоит на ShowDialog. Потоки при этом заняты своими делами и кидают сообщения. Первый поток — сообщения от него форма выводит без задержек, остальные потоки — сообщения на форму приходят, выполняется BeginInvoke, после чего — вывода данных нет до момента окончания потока. Все callback с потоков оформлены как асинхронные.
Переписал посылку данных с callback через SynchronizationContext вместо BeginInvoke — все стало отрисовывать.
0
Боюсь разочаровать велосепедистов, но реализация методов контекста синхронизации WinForms, делает велосипеды бессмысленными с точки зрения «скорости»/«принудительности» и т.п.
Контекст просто инкапсулирует элемент управления и вызывает у него BeginInvoke, да удобно с точки зрения, времени жизни объекта и переносимости кода, но со всех других точек зрения, это самообман.
Контекст просто инкапсулирует элемент управления и вызывает у него BeginInvoke, да удобно с точки зрения, времени жизни объекта и переносимости кода, но со всех других точек зрения, это самообман.
0
public override void Post(SendOrPostCallback d, object state)
{
if (this.controlToSendTo == null)
return;
this.controlToSendTo.BeginInvoke((Delegate) d, new object[1]
{
state
});
}
-1
Нет. Контекст WinForms работает по-другому, он посылает оконное сообщение потоку, без использования дескриптора окна. В этом его главный плюс.
0
UPD: извиняюсь, немного напутал. Он использует дескриптор окна — но не текущего, а скрытого. Но это все равно сильно отличается от «просто инкапсулирует элемент управления».
0
А чем отличается то? На старте создаем пустой контрол, который потом используем для межпоточных вызовов. Контрол спрятан, вызов BeginInvoke спрятан, чем не пример инкапсуляции?
0
Тем, что он это делает не «просто», а давая
1) гарантию времени жизни скрытого контрола — он будет жить даже когда не открыто ни одной формы,
2) удобный способ получения экземпляра — достаточно вызвать SynchronizationContext.Current, в то время как создать контрол с нужными свойствами — куда сложнее.
Кроме того, он
3) реализует абстракцию контекста синхронизации, которая не ограничивается исключительно инкапсуляцией контролов.
1) гарантию времени жизни скрытого контрола — он будет жить даже когда не открыто ни одной формы,
2) удобный способ получения экземпляра — достаточно вызвать SynchronizationContext.Current, в то время как создать контрол с нужными свойствами — куда сложнее.
Кроме того, он
3) реализует абстракцию контекста синхронизации, которая не ограничивается исключительно инкапсуляцией контролов.
0
Разговор шел, про скорость и прочие плюшки реализуемые контекстом, я просто показал, что внутри контекст есть все тот же вызов BeginInvoke. Что не отменяет его плюсы, описанные как мной, так и вами, но в рамках того, для чего он был использован в статье, его использование эквивалентно изначальному коду!
0
В рамках статьи — согласен. Но мне показалось, вы критикуете первый из комментариев к статье…
0
Глубже копать смысла нет, реализация в любом случае на базе обычной очереди сообщений, провисания UI из за множественных потоков быть не должно если у них приоритет не задран до небес. По сути любой код работающий в UI потоке работает в рамках сессии обработки сообщения из очереди, если никто не держит выполнение, все спланированные на выполнение задачи будут выполнены.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Windows Forms & Invoke from parallel threads