Предисловие


Тема Исключений (за и против) не нова, и уже не раз обсуждалась. Но всё же, я надеюсь, что каждый из прочитавших данную статью почерпнёт что-то новое и полезное для себя.

Причиной появления этой публикации стало нежелание больше молчать и просто смотреть, как интернет заполняют «всезнающие» программисты, которые учат новичков в нашей сфере забивать гвозди микроскопом, находя десятки аргументов в защиту своих методов! Потому, эта публикация направлена, скорее, на новичка постигающего программирование и задающего вопросы. Она является моим «правильным ответом»

Итак, что же такое Исключение (Exception)?


imageИсключение — это то, вероятность (возможность) чего исключается системой… это то что в условиях программы произойти не может.

Посмотрите в свой код. Можете ли вы к каждому исключению дописать «но ведь это невозможно» или «но это же исключено»? Думаю, мало кто сможет честно ответить «да». Если ваш ответ «нет» — значит часть исключений на самом деле не являются таковыми, просто вы использовали этот механизм, потому как вам показалось это более удобным. То самое «удобство» такого подхода будет рассмотрено далее.

Кто же должен определять, какие ситуации программа исключает, а какие нет?


Все исключения могут быть продиктованы только заданием или здравым смыслом, и ничем или никем иным. Исключение не определяется ни типом данных, ни их источником, хотя нередко можно услышать или прочитать в «умных» статьях: «Неправильный клиентский ввод не может быть исключением»… не верьте! Вот, просто, не надо в такое верить.

Вы можете в задании получить вполне разумное исключение неверного клиентского ввода: «С системой будут работать специально обученные менеджеры нашей компании, потому ввод неправильных данных исключён» или «Менеджер будет загружать в систему письма, которые уже прошли проверку правильности формата, поэтому формат всегда будет именно такой». Вот теперь заказчик сам вам сказал, где следует породить исключение в написанной для него программе (и ничего страшного, что это клиентский ввод). Разумеется, если речь всё же идёт о неком продукте, а не библиотеке, программу не надо просто ронять. Пускай на верхнем уровне висит обработчик исключений и превращает все необработанные исключения в ошибки, сохраняет данные и деликатно завершает выполнение (или передаёт управление основному модулю, если ошибка не в нём).

Но, как и обычно, не забываем читать между строк и уточнять задание. Если вас просят сохранить файл, уточните, возможна ли ситуация, что файл не сохранится. А если решили не уточнять, тогда не исключайте такую вероятность, а реализуйте реакцию на это событие. Ваш код не должен ни о чём умалчивать.

Или предусмотрите, или исключите.

Почему любое лишнее Исключение является вредным для кода?


Не раз сталкивался с ситуацией, когда отрицательный результат выполнения функции возвращали в виде исключения, хотя такой результат был предусмотрен самой задачей. На этот счёт слышал разные пояснения:

  • Не передавать же по всей цепочке вызовов флаг завершения
  • Мне надо передать данные, собранные до получения отрицательного ответа
  • Функция может вернуть отрицательный результат по трём причинам. Так проще передать эту причину
  • множество других доводов...

Почему же так не надо делать? Да потому, что:
image
  • Вы не сможете смело использовать уже написанные методы в других местах, ведь они могут порождать неожиданные исключения;

  • GOTO зло — потому что путает код, передавая управление в произвольную точку. Не следует думать, что передача управления, перепрыгивая произвольное количество вызовов в стеке, меньше путает ваш код, чем GOTO;

  • Кому-то, а возможно и вам самим, потом надо будет что-то исправить или дописать в написанном коде. Не следует считать, что через 2 года вы влёт вспомните, что этот модуль системы для отрицательных ответов использует исключения. То есть, вам придётся вникать… и вникать не только в код, но и во все «необычности» подхода.

Не стал включать в список выше такие аргументы как «это правило хорошего тона» и «давайте всё использовать по назначению», итак написано уже достаточно.

Заключение


Старался не вдаваться лишние подробности и в каждое предложение вкладывать максимум смысла. Надеюсь у меня это получилось, и читая эти строки, вы думаете: «пять минут, а столько нового!» Ну что же, надеюсь это так.

Напоследок хотелось бы сказать, что не следует изворачиваться под давлением факторов, сроков и «так же проще», и писать неправильный код, зато быстро.

Совершенство и отказоустойчивость нашего кода и определяет наш профессионализм.
Поделиться с друзьями
-->

Комментарии (43)


  1. vlreshet
    28.09.2016 13:42
    +8

    А я думаю «пять минут потраченные впустую», потому что в статье нет ни одного конктетного совета, а есть только общий и весьма очевидный посыл — «исключения это не серебряная пуля, использовать их или нет надо смотреть по ситуации».


    1. MacIn
      28.09.2016 16:06

      а есть только общий и весьма очевидный посыл

      Да ну? По-моему посыл как раз использовать исключения только для нештатных ситуаций, а не «смотреть по ситуации».


  1. pengyou
    28.09.2016 14:05
    -2

    Бизнес определяет эволюцию языков. Часто получается так: бизнес-требования противоречат инженерным требованиям. Значит, с точки зрения инженера, языки и экосистемы развиваются по абсурдной траектории, инженер видит отрицательный отбор. Но бизнесу можно всё, что приводит к увеличению нормы прибыли, а значит, можно и исключения, и GOTO, и плевать на идеалы инженеров тоже можно. «MVP» и всё, сиди пиши говнокод молча, а то вспомним про hire & fire.


  1. hdfan2
    28.09.2016 14:22
    +5

    Исключение — это то, вероятность (возможность) чего исключается системой… это то что в условиях программы произойти не может.

    Извините, но это чушь какая-то. Если это не может, то нет смысла и обрабатывать это. Может быть, лучше написать «произойти не должно»?


  1. pehat
    28.09.2016 14:28
    +5

    Вы не сможете смело использовать уже написанные методы в других местах, ведь они могут порождать неожиданные исключения

    Fail fast? Не, не слышал. Лучше молча глотать и утаивать, что что-то пошло не так.


  1. lair
    28.09.2016 14:29
    +2

    Исключение — это то, вероятность (возможность) чего исключается системой… это то что в условиях программы произойти не может.

    А откуда вы взяли это определение?


    Все исключения могут быть продиктованы только заданием или здравым смыслом, и ничем или никем иным. Исключение не определяется ни типом данных, ни их источником

    Вы так говорите, как будто тип и источник данных не определяются задачей.


    Если вас просят сохранить файл, уточните, возможна ли ситуация, что файл не сохранится. А если решили не уточнять, тогда не исключайте такую вероятность, а реализуйте реакцию на это событие. Ваш код не должен ни о чём умалчивать.

    Такая ситуация, очевидно, возможна. А вот теперь вопрос — зачем обрабатывать ее на каждом уровне кода (ведь "код не должен ни о чем умалчивать")?


    Не раз сталкивался с ситуацией, когда отрицательный результат выполнения функции возвращали в виде исключения, хотя такой результат был предусмотрен самой задачей

    Что такое "отрицательный результат выполнения функции"?


    Почему любое лишнее Исключение является вредным для кода?

    Как определить, является ли исключение лишним?


  1. NeoCode
    28.09.2016 14:38

    Исключения это интересная тема. Если где-то в недрах кода (возможно даже в сторонней библиотеке) возникла недопустимая ситуация — например невозможность выделить память — то нужно предпринять какие-то действия. По идее, это может быть и код ошибки, и исключение, и даже аварийное завершение работы всей программы. Но что именно?

    В какой-то момент мне пришла в голову мысль, что в языке программирования должна существовать некоторая конструкция, определяющая «ошибочную ситуацию». Нечто среднее между «return кода_ошибки» и «throw исключения». То есть библиотека говорит «у меня произошла ошибка», а как именно она должна быть обработана — это вопрос вызывающего кода. Ведь даже обработка исключений может быть разной (SEH, DWARF, SJLJ). В конце концов, код возврата — это тоже способ организации исключений, особенно если компилятор возьмет на себя часть этой работы (очевидно что для этого потребуется сказать компилятору какие коды означают ошибки и обеспечить тип возврата всех функций, участвующих в этой системе).

    То что любая функция может внезапно выкинуть любое исключение (как в С++) — это плохо и по сути является аналогом goto. Реализация в Java (с явным указанием исключений которые могут быть выброшены) лучше (именно тем что там исключения указываются явно). Но можно пойти еще дальше.

    В некотором роде исключения — это модуль языковой функциональности (как RTTI в C++, отключаемую опцией компиляции). В определенных случаях (например программирование под микроконтроллеры с малым объемом памяти) исключения целесообразно отключать вообще, переводя весь код на самую простую реализацию — кодами возврата. При этом хотелось бы, чтобы библиотеки были общие (т.е. не держать отдельные наборы библиотек для каждой реализации исключений).


    1. MacIn
      28.09.2016 16:08

      То что любая функция может внезапно выкинуть любое исключение

      Почему бы не ловить все исключения?


    1. Lure_of_Chaos
      28.09.2016 16:34

      Реализация в Java (с явным указанием исключений которые могут быть выброшены) лучше (именно тем что там исключения указываются явно).

      Не все с этим согласны


    1. Antervis
      29.09.2016 06:02

      В какой-то момент мне пришла в голову мысль, что в языке программирования должна существовать некоторая конструкция, определяющая «ошибочную ситуацию». Нечто среднее между «return кода_ошибки» и «throw исключения».

      в с++ есть std::error_code.


    1. vintage
      05.10.2016 00:57

      Это уже давно реализовано в LISP :-) http://schemer.in/aeh.html


  1. impwx
    28.09.2016 14:46
    +2

    «То, чего никогда не может быть» — это только необработанное исключение. Было бы странно вводить в большинство современных языков довольно сложный механизм try\catch\throw для случаев, которые вообще никогда не должны выполняться, не находите?

    В более широком смысле исключение описывает ситуацию, в результате которой локальный кусок кода не может дальше выполняться. В таком случае оно вполне логично «всплывает» до того уровня, где может быть осмысленно обработано. Это позволяет разделять ответственность, структурирует код и отлично сочетается со стековой архитектурой.

    В статье ни одного аргументированного довода против исключений не нашел. Всё сводится к тому, что это «сложно» или «не нравится». Сложность вижу только в том случае, если нет порядка и половина функций возвращает значения-ошибки, а половина кидает исключения. Если принять за правило, что все функции работают унифицированно — не запутаешься, и через два года не нужно будет ничего вспоминать.

    А дальше вообще начинаются «Вредные советы»:

    Пускай на верхнем уровне висит обработчик исключений и превращает все необработанные исключения в ошибки, сохраняет данные и деликатно завершает выполнение

    Когда исключение всплывает, любой уровень программы может узнать об этом и при необходимости пробросить его дальше. Вы же предлагаете ловить исключение на самом верхнем уровне, а дальше что — оповещать оттуда все необходимые компоненты? Это вывернет зависимости наизнанку, связность компонентов вырастет, а код станет неподдерживаемым.

    Вы не сможете смело использовать уже написанные методы в других местах, ведь они могут порождать неожиданные исключения;

    Если вы вернете из функции неизвестный код ошибки, ситуация будет еще опаснее. В лучшем случае вы всё равно получите исключение, только не осмысленное, а что-то типа «обращение к нулевому указателю» или «выход за пределы массива» при работе с некорректными данными. В худшем — ошибка будет проглочена и программа молча выполнит неожиданный код, вероятно повреждая при этом данные.

    Возможно, автор не до конца разобрался с механизмом исключений, или сознательно решил этого не делать из этических соображений?


    1. MacIn
      28.09.2016 16:09

      В худшем — ошибка будет проглочена и программа молча выполнит неожиданный код, вероятно повреждая при этом данные.

      С исключениями тоже так может быть — пустой catch/except блок.


  1. kahi4
    28.09.2016 14:55

    У исключений есть большое преимущество перед возвратом функции. Просто огромнейшее: стектрейс. Неожиданно вывалившееся исключение можно проверить, отдебажить, воспроизвести. Если функции начинают возвращать -1 — найти источник ошибки гораздо сложнее.


    А еще бывает так, что памяти нет ну совсем. Исключение пролетит через всю систему до того уровня, когда уже можно принимать решение о том, что делать дальше. Возврат кода ошибки же либо наплодит во всем трейсе лишние условия проверки, либо же попытается выполнять код дальше, вновь выделить память и зависнет намертво.


    Исключения — сильный механизм и его нужно применять с умом. К сожалению, помимо того, что он сильный, он еще и требовательный по ресурсам (добавляет много оверхеда), поэтому нужно понимать, когда нужно именно бросать исключение, а стоит сделать возврат из функции.


    1. VioletGiraffe
      28.09.2016 15:05
      +2

      ИМХО главная проблема исключений — нечитаемость кода. Очень сложно найти место, где исключение будет обработано. А ещё бывает, что где-то забыли catch, или написали там не тот конкретный тип исключения. И всё, вроде, работает, и даже сообщение об ошибке выдаётся, но ошибка обрабатывается не так, как было задумано. ИМХО программировать с использованием кодов возврата проще. Как минимум, до какого-то уровня сложности системы.


      1. vlreshet
        28.09.2016 15:31

        Не могу сказать про ситуацию в целом, но, допустим, в PHP — если всё делать «по фен-шую» то код должен бросать не стандартное исключение, а свой собственный класс исключения, который наследуется от встроенного. И потом не сложно найти где ловится именно наш тип исключения, а не исключение впринципе. Как-то так.


        1. VioletGiraffe
          28.09.2016 15:54
          +1

          В Java и С++ не запрещено ловить базовый тип, даже если бросается наследник (что логично). Скажем так: можно сделать нормально, но никто не мешает сделать коряво (что, впрочем, вряд ли является отличительной чертой исключений).


      1. lair
        28.09.2016 18:04
        +2

        ИМХО программировать с использованием кодов возврата проще.

        Вот только проигнорировать код возврата (во многих языках) проще, чем исключение.


      1. Antervis
        29.09.2016 06:17

        исключения как инструмент совершенно не уникальны в том, что при корявом их применении код становится сложным. Например, как только начинаешь использовать коды возврата, все функции по умолчанию начинают возвращать не bool/string/..., а код ошибки. И в конечном итоге оказывается, что при возникновении какой-то ошибки её код возврата тянется через всю программу, а обрабатываются они точно так же в ограниченном количестве мест. Архитектура получается точно такая же, как и с исключениями, но с исключениями проще писать как позитивный, так и негативный сценарий выполнения.

        Плюс, исключния более приятны когда пытаешься разбить большие функции


    1. MacIn
      28.09.2016 16:10
      +1

      У исключений есть большое преимущество перед возвратом функции. Просто огромнейшее: стектрейс.

      Это если оно unhandled и на самом деле исключение.


  1. vyatsek
    28.09.2016 16:01

    Извините чукча не читатель. Плохой учитель губит хороших учеников.
    «Все исключения могут быть продиктованы только заданием или здравым смыслом», о боже откуда это? А как же формализация?
    Исключения должны выбрасываться в том случае, если нарушаются одно из трех условий при вызове члена типа.
    Предусловие, инвариант, пост условие. Как это разумно и правильно применить в контекте проекта это уже другой вопрос.

    Предусловие — проверяет входные параметры и инвариант типа. Потом происходит выполнение каких-то операций, которые могут нарушать инвариант. Перед возвращением значения происходит проверка постусловий и инварианта.
    Это если в кратце.


    1. vyatsek
      28.09.2016 16:29

      Не успел отредактировать.
      Предусловия это условия при которых метод не сможет выполнить возложенную на него обязанность, обычно в предусловиях проверяют валидность входных данных.
      Пост условия это проверка условий, что метод выполнил корректно свою обязанность. Инвариант это условия которые должны выполняться всегда, исключение составляют методы в которых этот инвариант на время выполнения может быть нарушен.
      Например есть тип Employee, c методами IncreaseBonus и DecreaseBonus.
      Пусть будет объект employee с с бонусом равным 10. И кто-то например вызывает employee.DecreaseBonus(20), методы не имеют возвращаемого значения. Предусловие проверяет что 20 > 0, инваринат проверяет что текущее значение бонуса больше 0, 10>0.
      Потом происходит вычисление и бонус становится -10. Проверки на пост условие нет, хотя теоретически это может быть успешность записи еще куда либо, а инвариант проверят что бонус отрицательный и выбрасывает исключение. И объект находится в неконсистетном состоянии, при вызове любого другого метода инвариант должен выбрасывать исключение.
      Данный пример с точки зрения дизайна не является лучшим, но наглядно демонстрирует условия.
      В реальности примером инварианта в C# является правильная реализация интерфейса IDisposable, где перед каждым методом проверяет флаг isDisposed, другим примером может быть проверка isConnected при вызове в типах которые оперируют связью. Примеры предусловий это проверка аргументов на валидность значений.
      По поводу проверки и бросания исключений от пользовательского ввода, тут все определяется контрактом. Если модель не может работать с пустой строкой, то если значение передаваемого параметра пустая строка — исключение однозначно, чтобы исключения не возникало, об этом должен позаботиться UI — не давать применить такое значение.
      Вы также не затронули тему о иерархии исключений и их обработке, но этого вообще хватит на отдельный пост.
      Учите мат часть она давно уже написана.


  1. MacIn
    28.09.2016 16:03
    +3

    Вы не сможете смело использовать уже написанные методы в других местах, ведь они могут порождать неожиданные исключения;

    А что такое «неожиданное исключение» и почему оно является препятствием? По сути, это неожиданный код возврата. Функция возвращает вам, скажем, длину буфера, или -1 в случае неудачи. А вы не знали, что значение может быть отрицательным — это неожиданный для вас результат. Значит ли это, что код возврата нельзя использовать? Нет. С исключениями то же самое — выбрасываемые в виде кода ошибки исключения должны быть документированы, так же как и возвращаемые функцией значения.

    GOTO зло — потому что путает код, передавая управление в произвольную точку. Не следует думать, что передача управления, перепрыгивая произвольное количество вызовов в стеке, меньше путает ваш код, чем GOTO;

    Ничего подобного. Если так рассуждать, то мы должны отказаться от конструкций:
    for ()...{
    break;
    }

    if (){

    } else {

    }

    В первом случае у нас есть неявный GOTO вовне цикла, во втором — неявный GOTO к блоку ELSE.

    GOTO — не must die. Must die неправильное его использование. Самая большая проблема с goto — это прыжок назад по коду с созданием петли. Именно это тяжело отслежить и понять, именно из-за этого получается пресловутое спагетти. Исключение не бросает нас вверх по ходу исполнения, оно бросает нас вниз, дальше.

    Не следует считать, что через 2 года вы влёт вспомните, что этот модуль системы для отрицательных ответов использует исключения

    Такой же странный аргумент, как и первый. И тот же самый контр-аргумент. Вот вы надеетесь на то, что код ошибки — это -1. А потом добавили еще -2 и -3. Через поименованные константы, разумеется. А результат функции по-прежнему проверяете на == -1. И? Какая разница? Кто мешает вам ловить все коды ошибки меньше нуля или все исключения (определенные + «остальное»)?

    Использовать нужно то, что сделает ваш код читаемым и надежным. Лучше коды ошибок? Пожалуйста. Лучше исключения? На здоровье. Лучше goto с переходом в блок деинициализации в конце функции? Да рали бога.


  1. kroshanin
    28.09.2016 16:27
    +1

    В разработке руководствуюсь правилом: «исключения для программистов, а не для пользователей.»
    То есть если пользователь забыл ввести значение в поле формы — это просто ошибка, не исключение.
    А вот если где-то «внутри» очень важная переменная вдруг оказалась не заданной — нужно кидать исключение, причем вывести и имя функции, и сообщение, и значения других параметров, которые помогут найти эту ошибку.


  1. G-M-A-X
    28.09.2016 17:20

    >GOTO зло — потому что путает код, передавая управление в произвольную точку.

    В PHP не в произвольную, есть разумные ограничения.

    >Вот теперь заказчик сам вам сказал, где следует породить исключение в написанной для него программе

    Не в этих ли случаях их использовали, давая указанные вами пояснения? :)


  1. eXTreMeHawk
    29.09.2016 12:12

    Удивительно, но в статье ничего не сказано о том, что исключения ломают referential transparency. И это тот самый пункт номер 1 с которого надо начинать, когда мы говорим вообще что-либо об исключениях. А ещё они также не composable + не работают в multi-threaded окружении.


    1. lair
      29.09.2016 14:25

      Referential transparency — это хорошая такая штука, особенно в функциональном программировании, но вот только не всегда достижимая. Один раз у вас память выделилась, другой раз — нет.


      А ещё они [...] не работают в multi-threaded окружении.

      Работают. Просто надо уметь их готовить.


      1. eXTreMeHawk
        01.10.2016 08:56

        А есть подходы, которые не надо уметь как-то специально готовить и которые просто работают, при этом всегда и корректно… А в моменты когда они не работают корректно, программа просто не компилируется ;-)


        1. lair
          01.10.2016 12:43

          Что же это за волшебные подходы?


          1. eXTreMeHawk
            02.10.2016 07:52

            Вот здесь, если я правильно помню, было довольно хорошо про это рассказано: Railway Oriented Programming


            1. lair
              02.10.2016 11:18

              Вместо ссылки на часовое видео, можно было просто сказать "монада Try". Так вот, у меня для вас плохие новости.


              (а) монада Try не решает проблему referential transparency. Если внутри функции, пусть даже она возвращает Try[T], есть побочный эффект (например, работа с бд), функция не будет чистой, и, как следствие, referential transparency будет нарушена. Да фиг с ней, с БД, банальное выделение памяти — и ваша функция уже недетерминирована.


              (б) монада Try не решает проблему обработки ошибок в многопоточности: если вы положите функцию, возвращающую Try[T], в другой поток, и проигнорируете ее результат — как вы и делаете в другом комментарии, — вы ничего не узнаете о случившихся ошибках.


              (ц) в обоих этих случаях компилятор вам никак не поможет. Более того, прямо скажем, есть еще сильно больше одного способа "сломать" Try (впрочем, как и любой case class) даже при полной поддержке компилятором — и это еще не упоминая того факта, что описанная вами поддержка есть далеко не везде (скажем, в F# на необработанный кейс будет предупреждение, но код скомпилируется, в Scala, афаик, так же).


    1. G-M-A-X
      29.09.2016 14:30

      >не работают в multi-threaded окружении

      1. PHP, Node.JS — однопоточные. :) Нам можно :)
      2. А если войти в критическую секцию, то, кмк, все должно работать. Но сложновато. Как и любой другой многопоточный код. :)


    1. MacIn
      29.09.2016 17:05

      не работают в multi-threaded окружении.

      Это с какой стати? Вас не затруднит пояснить развернуто? Без каких-либо проблем используем исключения в многопоточных системах.


      1. eXTreMeHawk
        01.10.2016 08:51

        try {
          Task(some_work).run() // (1)
        catch {
          // (2) never reach this point
        }
        


        В примере выше, если Task запускает some_work в другом треде и в этом треде some_work породит исключение мы по дефолту в точке (2) его не поймаем никогда, если только не напишем специальную обвязку вручную, для проброса исключений из слейв-треда в мастер-тред.


        1. lair
          01.10.2016 12:46

          Ну так вы же проигнорировали результат Task.Run — так что неудивительно, что вы не получили информации о ходе выполнения. В таком коде, прямо скажем, some_work может никогда и не выполниться.


          1. eXTreMeHawk
            02.10.2016 07:47

            Это просто схемотичный пример, а не кусок кода, который должен компилироваться. В интернете погуглите пожулуйста «Referential transparency» и найдёте много информации почему исключения её ломают и, самое главное, как этого можно избежать с примерами кода и т. п.


            1. lair
              02.10.2016 11:08

              Я знаю, почему исключения ломают referential transparency, но мы-то здесь не о referential transparency, а о многопоточности.


              Так вот, в вашем коде полностью проигнорирован результат some_work — поэтому никакая обработка ошибок, включая монаду Try, про которую вы пишете рядом, вам не поможет. Более того, как я уже писал, поскольку результат проигнорирован, ваш код может завершиться раньше, чем some_work начнется. Ну да, такая вот многопоточность, только конкретно исключения тут ни при чем.


              1. eXTreMeHawk
                02.10.2016 11:35

                Я имел ввиду следующее. Сейчас попробую расписать подробнее. Допустим у вас есть поток B в котором происходит какая-то полезная работа. В какой-то момент в этом потоке B происходит ошибка и выбрасывается исключение. Это исключение может быть перехвачено и обработано только внутри потока B. Как поток A, который расчитывает, что от потока B рано или поздно придёт какой-то результат, узнает, что в B вычисление завершилось неудачей? По умолчанию никак. Но можно написать обвязку, которая будет прокидывать ошибку из потока B в поток A (тем или иным способом) и уже дальше в потоке А, например, кидать исключение. Это будет работать, но для этого придётся написать какой-то код. Ещё раз, исключение брошенное в одном потоке можно поймать и обработать только из этого же потока. По умолчанию другие треды про него ничего не узнают, если не предпринять специальных телодвижений.


                1. lair
                  02.10.2016 11:37

                  Как поток A, который расчитывает, что от потока B рано или поздно придёт какой-то результат, узнает, что в B вычисление завершилось неудачей?

                  Давайте начнем с простого вопроса: как поток A узнает, что от потока B пришел какой-то результат? По умолчанию это так не работает.


                  (вау, я только что нашел фундаментальную проблему в операторе return)


                1. MacIn
                  02.10.2016 17:03

                  Как поток A, который расчитывает, что от потока B рано или поздно придёт какой-то результат, узнает, что в B вычисление завершилось неудачей? По умолчанию никак

                  Воу-воу-воу, секундочку. Если мы ожидаем результата от потока Б, то мы ожидаем какого-то события, сигнализирующего достижения результата, или окончания потока Б. Т.е. в любом случае у нас будет код, который является прослойкой между двумя этими потоками. Так или иначе. Кто мешает этой прослойке проверять исключения? Например, что-то вроде
                  WaitForSingleObject(hThreadBEvent)
                  if lThreadB.Outcome == oFailure 
                    CheckException(lThreadB)....
                  ...
                  ...
                  Thread.HandleException(Exception){
                  Terminate;
                  Outcome = oFailure;
                  SetEvent(hmyEvent)
                  }
                  


                  1. lair
                    02.10.2016 20:05

                    Прямо скажем, .net-овский Task, который местная реализация монады Future, как-то так и делает (не по реализации, а по результирующему поведению):


                    var task = Task.Run(somework);
                    //...
                    await task; // завершится только тогда, когда somework закончен, и если в somework был эксепшн, он будет выкинут здесь


        1. MacIn
          01.10.2016 14:12
          +1

          Так, простите, где здесь пример того, что исключения не работают в многопоточном окружении?
          Если исключение будет порождено методом run, оно поймается. Исключения, порождаемые some_work'ом, должны ловиться исключением обертки Task, запускающей отдельный поток.


    1. Antervis
      03.10.2016 06:42

      функция, которая может то выполниться, то зафейлиться, в принципе не может быть чистой. Просто при использовании исключений она выглядит чистой, а ошибки и результат выполнения позитивного сценария обрабатываются в разных местах