Сюжет. Работает поток A, исполняющий большой объём кода (рассчёт чего-либо). На любом этапе нужно обновлять пользовательский интерфейс — сообщать о текущем состоянии процесса, обрабатывать Cancel.
Из-за этого в очень многих местах приходится вставлять код вроде
m_pUICtx->UpdateStatus(currentStage, currentPercent, ...);
if (m_pUICtx->IsCancelled())
{
cleanup();
return ...;
}
Предложение.
Это плохо. Мне пришла в голову хорошая идея.
Если можно было бы из внешнего UI-потока B вытолкнуть исключение, которое отработаыло бы в контексте потока A, то можно было бы такие вещи творить!
Весь код потока A можно было бы очистить от кода связанного с UI.
При возникновении исключения на любом этапе наступает Cancel. Если нужно, поток A может перехватить это исключение чтобы сделать нужные подчистки.
Для обновления состояния UI можно было бы использовать SEH исключения:
поток A
int report_progress(int i)
{
pUICtx->Update(i);return EXCEPTION_CONTINUE_EXECUTION;
}
void A_worker()
{
int i = 0;
__try
{
i++;
// здесь внешний код вызывает UpdateUI_Exception
i++;
i++;
...
}
__except(report_progress(i))
{
}
}
Идея мне очень понравилась. На её основе можно было бы решить множество красивых задач — обработка UI это самая очевидня среди них.
Собственно, вопрос: можно ли как-то вытолкнуть исключение (C++ и\или SEH) в другом потоке?
Не думаю. C>Если можно было бы из внешнего UI-потока B вытолкнуть исключение, которое отработаыло бы в контексте потока A, то можно было бы такие вещи творить!
Посмотри в сторону ATL механизма thunk и GetThreadContext/SetThreadContext. Т.е. делаем SuspendThread, правим EIP на ф-ю котор. кидает исключения, а потом ResumeThread.
Но все это чистое хакерство, кроме того весьма граблеопасное. Так что вещей можно действительно натворить. Тут уже эта тема всплывала, поищи.
А не проще ли
C>
C>поток A
C>void report_progress(int i)
C>{
C> pUICtx->Update(i);
C>}
C>void A_worker()
C>{
C> int i = 0;
C> {
C> i++;
// здесь внешний код ставит флажок (синхр. опущена для простоты)if (some_ui_flag)
{
pUICtx->Update(i)
//сбросим флажек
}
C> i++;
C> i++;
C> ...
C> }
C>}
C>
Здравствуйте, Patalog, Вы писали:
C>>Это плохо. Мне пришла в голову хорошая идея. P>Не думаю.
C>>Если можно было бы из внешнего UI-потока B вытолкнуть исключение, которое отработаыло бы в контексте потока A, то можно было бы такие вещи творить! P>Посмотри в сторону ATL механизма thunk и GetThreadContext/SetThreadContext. Т.е. делаем SuspendThread, правим EIP на ф-ю котор. кидает исключения, а потом ResumeThread. P>Но все это чистое хакерство, кроме того весьма граблеопасное. Так что вещей можно действительно натворить. Тут уже эта тема всплывала, поищи.
Ммм. А можно поподробнее? В MSDN связанно с thunks — тока какой-то страх с 16-битным кодом...
Это оно?
P>А не проще ли
Не проще. Чтобы не делать такого, всё и было задумано.
Здравствуйте, Chez, Вы писали:
C>Привет.
C>Сюжет. Работает поток A, исполняющий большой объём кода (рассчёт чего-либо). На любом этапе нужно обновлять пользовательский интерфейс — сообщать о текущем состоянии процесса, обрабатывать Cancel. C>Из-за этого в очень многих местах приходится вставлять код вроде
.....
В данный момент я как раз решаю подобную задачу.
Разработал спец. метод и назвал его "Тикающая функция" (блин прям как "Парящий дракон")
Создается трид в него передается указатель на объект, трид внутри содержит цикл каждый раз вызывающий ту самую Tick().
Tick() — многократно вызывемая функция, делающая за один такт очередной (НЕБОЛЬШОЙ ПО ВРЕМЕНИ) шаг в работе working thread.
Применительно к твему примеру могла бы делать Tick() {i++;}
после каждого такта в цикле проверяется флаг останова (апдейта, перенаправления и пр.), цикл остановится (отработает апдейт, перенаправление и пр.) если установить во внешнем объекте флаг.
Эта схема уже удачно применяется. И работает без нареканий.
Здравствуйте, <Аноним>, Вы писали:
А>Эта схема уже удачно применяется. И работает без нареканий.
А как в таком случае будет выглядеть реализация функции Tick()?
Я так понял ты просто хочеш оповестить главный поток о состояниях в рабочем потоке?
Это можно сделать с помощью посылки сообщений (PostMessage)
Тем самым ты избавляешь главный поток от необходимости постоянно опрашивать
рабочий поток.
Здравствуйте, <Аноним>, Вы писали:
А>Я так понял ты просто хочеш оповестить главный поток о состояниях в рабочем потоке? А>Это можно сделать с помощью посылки сообщений (PostMessage) А>Тем самым ты избавляешь главный поток от необходимости постоянно опрашивать А>рабочий поток.
Да, но таким образом я заставляю рабочий поток постоянно уведомлять главный поток.
Сделать чтобы главной поток опрашивал — запросто.
А чтобы рабочий поток отпралял — нет: представьте что в нём задействован мегабайт исходников, вычисляющих что-либо.
И ввобще, код, связанный с алгоритмами, вачислениями и т.д. никакого отношения к UI иметь не должен. Он должен о нём просто не знать.
хъ
C>Ммм. А можно поподробнее? В MSDN связанно с thunks — тока какой-то страх с 16-битным кодом... C>Это оно?
Нет. См. напр. _stdcallthunk в atlbase.h. Также в местных форумах переодически эта тема всплывала в связи с "нестатический метод класса как оконная ф-я". Воообще-то оно тебе не для данной задачи не особо и надо, я ее пользовал в подобном случае сугубо для удобства.
P>>А не проще ли
C>Не проще. Чтобы не делать такого, всё и было задумано.
Ну не знаю. Вопрос вкуса наверное.
А ты, как я понял, вообще не ищещ легких путей и любишь подобные штучки на грани фола.
хъ
C>А чтобы рабочий поток отпралял — нет: представьте что в нём задействован мегабайт исходников, вычисляющих что-либо.
C>И ввобще, код, связанный с алгоритмами, вачислениями и т.д. никакого отношения к UI иметь не должен. Он должен о нём просто не знать.
Хм, может тебе подумать в торону запустить этот мегавычислительный участок кода как отдельный отлаживаемый процеесс?
Здравствуйте, Patalog, Вы писали:
P>Хм, может тебе подумать в торону запустить этот мегавычислительный участок кода как отдельный отлаживаемый процеесс?
Может. Можно поподробнее?
Здравствуйте, Chez, Вы писали:
C>Здравствуйте, <Аноним>, Вы писали:
C>И ввобще, код, связанный с алгоритмами, вачислениями и т.д. никакого отношения к UI иметь не должен. Он должен о нём просто не знать.
Ну и что?
К UI это прямого отношения не имеет.
Тебе нужен механизм оповещения, который является
платформенно независимым и не привязанным к UI в этом самом рабочем потоке.
Это все нормально можно организовать.
Уж точно будет лучше выкидывания исключений....
Рабочий поток все равно должен оставлять какие-то следы
чтобы основной поток знал о них и мог бы как-то их визуализировать.
Если рабочий поток не оставляет следов и не шлет никаких сообщений,
то у тебя есть только момент начала работы и момент завершения работы потока.
В этом случае ничего другого ты не получишь и в UI ничего и не покажешь.
[]
P>>Хм, может тебе подумать в торону запустить этот мегавычислительный участок кода как отдельный отлаживаемый процеесс?
C>Может. Можно поподробнее?
Посмотри на DebugBreakProcess (увы, но она только для ХР и далее). Т.е. запускаешь процесс как отлаживаемый, а когда тебе нужно обновление брякаешь его, ловишь исключение в WaitForDebugEvent читаешь нужные данные ReadProcessMemory и потом ContinueDebugEvent. Можно еще попробовать ловить отладочное исключение в самом рабочем процессе а потом предавать нужные данные via RaiseException либо OutputDebugString. Все это на гране, но тем не менее вполне легально. Еще можно попробовать SuspendThread, FlushInstructionCache, вписать int 3 с помощью WriteProcessMemory и ResumeThread. Но это уже хак, имхо.
Но это все гипотетически сам я ничего такого делать не пробовал.
Здравствуйте, Chez, Вы писали:
C>Здравствуйте, <Аноним>, Вы писали:
А>>Эта схема уже удачно применяется. И работает без нареканий. C>А как в таком случае будет выглядеть реализация функции Tick()?
Эта функция за каждый такт выполняет очердной этап решаемой задачи (если надо обработать кучу файлов, к примеру, то за такт можно обрабатывать один или часть одного файла), после чего сохраняет текущее состоянее в статических переменных и доджыдается начало следующего такта, что-бы продолжить работу
Есть поток, который иногда затыкается в чужой библиотечной функции. Почему затыкается — второй вопрос. Не хочется просто прибить его. Как бы заставить его выйти из этой функции ?