Создаётся поток с помощью UnsafeQueueUserWorkItem, в потоке создаётся формочка, в формочке что-то рисуется... не важно.
Формочка уничтожается, поток завершает свою работу (Формочка есть обычный сплеш-скрин). После этого работает основное приложение, так вот, если приложение свернуть и не трогать его минут 5, оно больше не разворачивается. Если тоже самое сделать через System.Threading.Thread то всё работает замечательно. И ещё один ньюанс: зависание происходит, только если запускать приложение не из под студии, вне зависимости от сборки (debug или release). Очень бы хотелось узнать про эту странность UnsafeQueueUserWorkItem.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Здравствуйте, xdeltax, Вы писали:
X>Создаётся поток с помощью UnsafeQueueUserWorkItem, в потоке создаётся формочка, в формочке что-то рисуется... не важно.
X>Формочка уничтожается, поток завершает свою работу (Формочка есть обычный сплеш-скрин). После этого работает основное приложение, так вот, если приложение свернуть и не трогать его минут 5, оно больше не разворачивается. Если тоже самое сделать через System.Threading.Thread то всё работает замечательно. И ещё один ньюанс: зависание происходит, только если запускать приложение не из под студии, вне зависимости от сборки (debug или release). Очень бы хотелось узнать про эту странность UnsafeQueueUserWorkItem.
Насчет UnsafeQueueUserWorkItem не подскажу, но в .NET v1.1 (беглый взгляд на код .NEt v2 показал, что проблема вроде осталась ) был след. баг (фича?), который проявлялся именно при связке splash-screen\поток, связанный с классом
SystemEvents, а именно (см. комментарии):
/// <summary>
///Инициализирут окружение приложения
///<b>NOTE</b>: этот метод должен быть вызван при старте приложения в главном потоке!!!
/// </summary>
private static void InitializeEnvironment()
{
//
//HACK!!! (возможно баг .NET framework 1.1).
//форсируем инициализацию SystemEvents!!!
//
//зачем все это нужно:
//1) дело в том, что класс SystemEvents использует отложенную инициализацию,
//т.е. его инициализация происходит при первом обращении к его методам (свойствам,событиям).
//в процессе инициализации SystemEvents создает скрытое окно, которое используется
//для получения системных событий, (hint) это окно создается в потоке, в котором происходит инициализация
//(на самом деле это происходит, только если процесс может взаимодействовать с пользователем и текущий поток имеет ApartmentState.STA,
//в противном случае создается вспомогательный поток).
//Соотв., если инициализация SystemEvents произойдет в потоке, отличном от главного потока приложения (GUI-потока),
//то после завершения этого потока SystemEvents будет в неккоректном состоянии, что чревато неожиданными ошибками
//(описатель окна станет не валидным, со всеми вытекающими..).
//2) статические методы этого класса не являются thread-safe, несмотря на то, что в документации написанно обратное
//
//где проблема проявилась:
//при показе SplashScreen отдельном потоке.
//
//обходной путь для решения проблемы:
//подписываясь на событие SystemEvents.SessionEnded (или любое другое системное событие) мы принуждаем
//SystemEvents форсировать создание окна для получения системных событий в текущем (главном) потоке
SystemEvents.SessionEnded += new SessionEndedEventHandler(SystemEvents_SessionEnded_DummyCallback);
SystemEvents.SessionEnded -= new SessionEndedEventHandler(SystemEvents_SessionEnded_DummyCallback);
}
private static void SystemEvents_SessionEnded_DummyCallback(object sender, SessionEndedEventArgs e)
{
//nothing do...
}
Возможно это ваш случай, а разница в поведении между
System.Threading.Thread и потоком из пула может быть связанна с тем,
что callback (переданный в UnsafeQueueUserWorkItem) вызывается раньше, чем получает управление метод потока (ThreadStart) и как раз отрабатывает описанный выше сценарий (хотя поток в пуле может 'жить вечно', и возможно работает другой сценарий).
ps: если после старта splash-screen'а исп-я
System.Threading.Thread поставить паузу (
Thread.Sleep),
перед запуском
Application.Run, по идее ошибка должна проявиться.
pps: выделенное (2) в .NET v2 вроде починили...
Здравствуйте, AlexZu, Вы писали:
AZ>ps: если после старта splash-screen'а исп-я System.Threading.Thread поставить паузу (Thread.Sleep),
AZ>перед запуском Application.Run, по идее ошибка должна проявиться.
Я почему-то тоже думал, что в пуле есть вечная жизнь, и это виной всему... Но вечный слип не вызвал проявление ошибки
Кстати, есть такое предположение, что после закрития окна фреймвёрк не освобождает все рессурсы, захваченные окном (даже после "прохода" GC, вызова dispose....). Так вот, может окно получает какие-то сообщения уже после "смерти". Тогда получается, что когда работаем через пул — окно как бы формально принадлежит потоку пула, и винда пытается бросить какое-то сообщение в поток пула, который уже занят чем-то другим (этим и объясняется задержка времени, перед зависанием). А когда работаем через "персональный" Thread — он никогда не может быть отдан никому другому, по этому даже если винда бросает сообщение "умершему" окну, оно как бы "корректно" обрабатывается....