В статье про реализацию задачи про обедающих философов посредством Actors и CSP я отдельно затронул тему обеспечения exception-safety при использовании модели CSP. И, пожалуй, можно на этом моменте остановится подробнее еще раз.
Давайте представим себе, что мы захотели сделать функцию run_simulation() безопасной по отношению к исключениям.
Первое, что нам придется сделать -- это выполнить рекомендации из статьи по корректному завершению нитей для вилок. Т.е. сперва мы будем закрывать каналы, созданные для вилок, только потом будем вызывать join(). OK. С этим все понятно.
Далее нам нужно будет при возникновении каких-либо проблем завершить нити для философов.
И тут, если мы просто вызовем join(), мы опять наступим на те же грабли, не лежащие несколько по-другому. Дело в том, что внутри philosopher_process есть цикл, в котором выполняются вызовы receive(). Выход из receive() может произойти либо при получении ответа от вилки, либо при закрытии канала.
Но, если нити для вилок уже завершили свою работу, то вилки прислать ответ философу уже не смогут. И канал никто не закроет, т.к. каналом владеет сам philosopher_process, а run_simulation() к каналу доступа не получит.
Значит, каналы для философов мы так же должны создавать в run_simulation(), хранить их в контейнере, а потом принудительно закрывать прежде чем вызывать join() для нитей философов.
OK. Это уже шаг в верном направлении.
Допустим, что мы это сделали. Станет ли наше решение корректным?
К сожалению, нет. Т.к. внутри philosopher_process цикл с вызовами receive(). Из receive-то мы выйдем принудительно закрыв канал. А вот из цикла?
А из цикла мы не выйдем, т.к. для этого нужно увеличивать meals_eaten, а это происходит только при получении taken_t от правой вилки. Но ведь taken_t мы не получим, т.к. и вилки уже перестали работать, и наш канал уже закрыли.
Так что придется добавлять реакцию на закрытие канала, выставлять некий флаг завершения работы и добавлять проверку этого флага в условие цикла. Код, демонстрирующий как это может выглядеть, под катом.
В качестве сухого остатка повторю то, что говорил уже неоднократно: программировать многопоточность сложно. Особенно когда приходится работать с голыми нитями. Поэтому лично я предпочитаю использовать акторов, даже если это приводит к более многословным решениям. Все-таки при использовании акторов граблей немного поменьше.