[C++] Исключения и потоки
Если ты не перехватишь исключение в потоке, то грохнется он сам, процесс его создавший и все потоки порождённые этим процессом.
Странно, под виндой умирает только тот поток, в котором не перехвачено исключение.
У венды дебильная терминология, это же процесс получается, а не поток.
Имхо, микрософтовое апи связанное с многопоточностью - кал.
---
...Я работаю антинаучным аферистом...
CAPSLOCK!
Поражают люди, которые, не освоив технологию, пытаются с лёту её обосрать.
Не понял? Это в Линуксе не было потоков до какой-то версии ядра. В винде с этим всё в порядке. В общем, автору следовало указать ОС.
Конкретно меня интересуют семейства ОС Windows и UNIX/Linux
Может быть, стоит просто ловить исключения?
Страуструп писал, что нельзя рассчитывать на то, что exception сгенерированный в одном потоке может быть перехвачен в другом. Так что по идее exception надо перехватывать в том же потоке. Насколько я понимаю, неперехваченный exception может убить как весь процесс так и только тот поток, в котором он сгенерирован, - зависит от реализации exception handling.
Само собой. Только я сначала как-то не рассчитывал перехватывать исключения в том же потоке, где они возникли. Не продумал всю эту хрень.
А где именно ты их рассчитывал перехватывать и как? Просто интересно...
Типа
try
{
create_obj1;
create_obj2;
.....
obj1->start;
....
wait_for_object_exited;
}
catch(...)
{
}
Т.е. некоторые исключения перехватывались бы в самих потоках, часть возможно бы в описанным выше catch. Ну это так, без деталей мне в таком виде представлялась. Я тогда еще не рассуждал о том, в каком состоянии будет поток после вызова исключения, где оно вообще может пойматься и когда.
исключение приводит к вызову особо запомненного продолжения,
ну, или к восстановлению стеков до запомненного состояния.
А дальше работает либо прозрачность по ссылкам,
либо большая память (или извращённое воображение) программиста.
Не пойму, отчего нужно заводить обработчик в каждом потоке,
если при исключении поток не должен продолжаться.
---
...Я работаю антинаучным аферистом...
Не пойму, отчего нужно заводить обработчик в каждом потоке,
Потому что вот это -
если при исключении поток не должен продолжаться.в общем случае неверно. Да и в достаточно большой части частных тоже.
При этом задача оборачивания потока в трай-котч обёртку с произвольным способом информирования основного потока об исключении решается исключительно просто средствами любого языка, в котором есть понятие указателя/ссылки на функцию.
Это если мы о виндовых потоках говорим.
> Потому что вот это -
>> если при исключении поток не должен продолжаться.
> в общем случае неверно. Да и в достаточно большой части частных тоже.
Ну и что?
Допустим, что поток не должен продолжаться.
Почему его надо обёртывать в отдельный CATCH?
Вопрос ставится так: к каким семантическим трудностям
приводит использование THROW в многопоточной среде?
А именно: к каким трудностям приводит THROW из потока,
когда CATCH сделан в операторной задаче.
Например, очевидно, что способ Милендорфа (EuroForth-98) просто так не работает:
надо запрещать переключение задач перед нелокальным переходом.
С другой стороны, если возникают такие трудности,
то лучше не париться, а просто делать по CATCH на каждый поток ---
не так много это весит, чтобы так заморачиваться.
---
...Я работаю...
The Inner-Platform Effect is a result of designing a system to be so customizable that it ends becoming a poor replica of the platform it was designed with. This "customization" of this dynamic inner-platform becomes so complicated that only a programmer (and not the end user) is able to modify it.
Это, конечно же, относится не только к end-user приложениям.
http://dec.bournemouth.ac.uk/forth/euro/ef98/milendorf98.pdf
Я считаю, что насильники навряд ли заморочились на что-нибудь сложнее.
Основная мысль:
устранять возникающие трудности при использовании THROW между потоками не имеет смысла,
потому что простой способ реализации, наподобие милендорфова, неоправданно сильно усложняется,
а семантика THROW между потоками всё равно не очень хорошо определяется.
---
...Я работаю антинаучным аферистом...
P.S. А ещё меня в последнее время прут структуры Эртля (1, 2).
Даже Страуструпу, наверное, такое не могло бы присниться:
что всякая структура строится инкрементно с пустой.
Я считаю, что насильники навряд ли заморочились на что-нибудь сложнее.
Основная мысль:
устранять возникающие трудности при использовании THROW между потоками не имеет смысла,
потому что простой способ реализации, наподобие милендорфова, неоправданно сильно усложняется,
а семантика THROW между потоками всё равно не очень хорошо определяется.
---
...Я работаю антинаучным аферистом...
P.S. А ещё меня в последнее время прут структуры Эртля (1, 2).
Даже Страуструпу, наверное, такое не могло бы присниться:
что всякая структура строится инкрементно с пустой.
устранять возникающие трудности при использовании THROW между потоками не имеет смысла,ыыыыыыыы а ты вообще читал мои посты? Я именно об этом и говорю, вообще-то =)
потому что простой способ реализации, наподобие милендорфова, неоправданно сильно усложняется,
а семантика THROW между потоками всё равно не очень хорошо определяется.
Может быть, там есть какой-то не очевидный на первый взгляд способ?
Мало ли хаков может быть?
Мож там есть какая-нибудь "true black magic?"
---
...Я работаю антинаучным аферистом...
Дваццать первый век уже шесть лет как наступил, между прочим.
Я не знаю, конечно, как в форте реализуются потоки.
Но в знакомых мне парадигмах программирования Silver Bullet (aka True Black Magick) вряд ли существует - потому что межпоточное взаимодействие может происходить туевой хучей способов, а любая надстройка будет покрывать лишь некоторую часть из них, во всех остальных случаях либо программер будет возвращаться к своему underlying framework (нарушая единообразность кода либо будет плодить жутких чудовищ с использованием предложенной надстройки. Перечитай описание Inner Framework Antipattern.
Хотя если говорить конкретно про винду и С++, то вполне можно написать небольшую библиотечку, которая будет инициировать вызов переданной ей функции в отдельном потоке и вызывать Специальный Хендлер(так же переданный ей) в основном потоке (через message queue) если там где-то случился непойманный эксепшен. И пусть вызывающий сам заморачивается созданием специального флажка.
А ещё я не знаю, как оно происходит в дотНете. Вроде как выкинувший эксепшен тред останавливается, зато при попытке узнать его состояние можно заодно проверить его на причину останова. Не знаю.
Рассказать, как они делаются?
> Я не знаю, конечно, как в форте реализуются потоки.
Кто как хочет, стандарта на них нет.
"True Black Magic" --- это не "silver bullet."
Это такой грязный хак, который просто и изящно делает то, что требуется.
Хорошим примером такого хака является сишный "n&(n-1)."
На механизм IPC throw не опирается, вообще говоря.
Ну, разве что в каких-то извращённых способах.
> можно написать небольшую библиотечку
Всё же вопрос стоит не в том, как обойти, а возможно ли определить так,
чтобы было однозначно и более-менее интуитивно понятно.
Желательно, чтобы это было возможно сделать на уровне языка.
---
...Я работаю антинаучным аферистом...
Если в главом потоке set_terminate функцию, она будет действительна также и для всех остальных потоков, или в каждом потоке задается своя, или опять-таки от реализации зависит?
Т.е. самый удобный способ - ловить исключение в потоке и уведомлять об этом нужную сущность?
1. user-space -- потоки внутри одного пользовательского процесса. Разруливанием потоков занимается пользовательский код (библиотека). Естественная реализация, имхо, приводит к тому, что неперехваченное исключение грохает весь процесс.
2. kernel-space -- потоки рулятся на уровне ОС и похожи на отдельные процессы с общей памятью. Естественное поведение, имхо, что грохается один поток.
3. Комбинированные подходы.
Далее, под Linux существует несколько реализаций потоков.
Более подробная информация, возможно, содержится в стандартах типа Posix.
В любом случае, имхо, позволять исключениям пересекать границы потока -- без мазы.
> что неперехваченное исключение грохает весь процесс.
А знаешь, если неполениться и сделать копию стеков, то это не так.
---
...Я работаю антинаучным аферистом...
![](/images/graemlins/smile.gif)
Имхо-имхой, но лучше ловить исключения внутри потока, так и с программой будет меньше гемора.
1. user-space -- потоки внутри одного пользовательского процесса. Разруливанием потоков занимается пользовательский код (библиотека). Естественная реализация, имхо, приводит к тому, что неперехваченное исключение грохает весь процесс.imho исключения --- это чисто фича языка (C++, ML или какого хотите). ОС ни про какие исключения не знает. Для ОС поток и процесс --- контекст + набор машинных инструкций. ОС не знает складывают ли они там числа, или же обрабатывают исключения. Соответственно, не вижу никакой связи между user/kernel-вещами и исключениями.
2. kernel-space -- потоки рулятся на уровне ОС и похожи на отдельные процессы с общей памятью. Естественное поведение, имхо, что грохается один поток.
imho попытка обучить ОС понятию исключения --- заведомо порочная идея
Имхо-имхой, но лучше ловить исключения внутри потока, так и с программой будет меньше гемора.Согласен. Я бы тоже так делал в своей программе. Но автора треда интересует именно случай с другим потоком, поэтому его и обсуждаем. "Такая постановка задачи"
---
...Я работаю антинаучным аферистом...
Если под естественной реализацией понимать легковесный fork, то это не такimho это уже продвинутая реализация. Мне в свое время приходилось работать с таким http://www.gnu.org/software/pth/
Автору программы легче переделать код на отлов исключения внутри потока и закрыть тред.
Я вообще думал, что коли у потока есть свой отдельный стек, то он должен раскрутиться до конца, после чего будет выход из функции потока и он просто завершится. Но на деле не так.
Я так не считаю.
Если ОС поддерживает высокоуровневые понятия, это очень даже хорошо.
По крайней мере, не надо переизобретать очень многие велосипеды.
---
...Я работаю антинаучным аферистом...
+1
> Если ОС поддерживает высокоуровневые понятия, это очень даже хорошо.
> По крайней мере, не надо переизобретать очень многие велосипеды.
1) Есть рабочие примеры, где это сделано так, что не вызывает реакции "уж лучше бы они этого не делали"?
2) Исключения у всех разные. У C++ - одни, у ML - другие, Haskell - третьи. Чьим исключениям будем обучать ОС? Если C++, то чем хуже остальные? Привести всех под один бинарный формат не получится.
3) ОС и так очень сложная программа (миллионы строк кода). Если вводить туда высокоуровневые понятия все еще в 10 раз усложнится. Почему ты считаешь, что это целесообразно?
> что не вызывает реакции "уж лучше бы они этого не делали"?
Пока что PalmOS API, за исключением ориентации на Си, особых нареканий не вызывает.
> 2) Исключения у всех разные. У C++ - одни, у ML - другие, Haskell - третьи.
> Чьим исключениям будем обучать ОС? Если C++, то чем хуже остальные?
Зависит от основного языка разработки.
> 3) ОС и так очень сложная программа (миллионы строк кода).
> Если вводить туда высокоуровневые понятия все еще в 10 раз усложнится.
> Почему ты считаешь, что это целесообразно?
Потому что вижу хороший пример ПалмОС,
где не нужно придумывать всякие X, Tk и прочую бодягу.
---
...Я работаю антинаучным аферистом...
Я не знаю, какую реакцию должно вызывать SEH в винде, но оно явно рабочее.
...
HANDLE g_hEvent = NULL;
HANDLE g_hSemaphore = NULL;
volatile LONG g_nCount = 0;
LONG g_nThreadCount = ...;
...
void Sync
{
if (::InterlockedIncrement(&g_nCount) == g_nThreadCount)
::SetEvent(g_hEvent);
else
::WaitForSingleObject(g_hEvent, INFINITE);
if (::InterlockedDecrement(&g_nCount) == 0)
{
::ResetEvent(g_hEvent);
::ReleaseSemaphore(g_hSemaphore, g_nThreadCount-1, NULL);
}
else
::WaitForSingleObject(g_hSemaphore, INFINITE);
}
...
g_hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
g_hSemaphore = ::CreateSemaphore(NULL, 0, g_nThreadCount-1, NULL);
...
: explain ?dup if cr count type then ;
Теперь можешь писать:
c" True Black Magic!" throw
---
"Расширь своё сознание!"
Не вкурил, зачем здесь семафор? Почему нельзя обойтись вторым евентом?
Не вкурил, зачем здесь семафор? Почему нельзя обойтись вторым евентом?Чтобы не добавлять ещё один счётчик.
а где восстанавливается счетчик семафора?
![](/images/graemlins/smile.gif)
тогда почему нельзя сделать
void Sync
{
if (::InterlockedIncrement(&g_nCount) == g_nThreadCount)
{
g_nCount = 0;
::ReleaseSemaphore(g_hSemaphore, g_nThreadCount-1, NULL);
}
else
::WaitForSingleObject(g_hSemaphore, INFINITE);
}
void Sync
{
if (::InterlockedIncrement(&g_nCount) == g_nThreadCount)
{
g_nCount = 0;
::PulseEvent(g_hEvent);
}
else
::WaitForSingleObject(g_hEvent, INFINITE);
}
тогда почему нельзя сделатьПотому что один поток у тебя закроет открытый семафор сразу несколько раз.
или даже такА так у тебя не сбрасывается event.
PulseEvent
Sets the specified event object to the signaled state and then resets it to the nonsignaled state after releasing the appropriate number of waiting threads.
чо?Да много чего.
![](/images/graemlins/smile.gif)
Note This function is unreliable and should not be used. It exists mainly for backward compatibility. For more information, see Remarks.
A thread waiting on a synchronization object can be momentarily removed from the wait state by a kernel-mode APC, and then returned to the wait state after the APC is complete. If the call to PulseEvent occurs during the time when the thread has been removed from the wait state, the thread will not be released because PulseEvent releases only those threads that are waiting at the moment it is called. Therefore, PulseEvent is unreliable and should not be used by new applications.
For a manual-reset event object, all waiting threads that can be released immediately are released. The function then resets the event object's state to nonsignaled and returns.
For an auto-reset event object, the function resets the state to nonsignaled and returns after releasing a single waiting thread, even if multiple threads are waiting.
значит сбрасывается?
Да, не разглядел что PulseEvent но всё равно так писать нельзя. Я отредактировал пост с объяснениями.
Оставить комментарий
erotic
Что будет происходить, если я вызову исключение не в основном потоке программы? Что будет с потоком, могу ли я перехватить это исключение в главном или каком-либо другом потоке?