Является ли Try / Наконец безопасным для исключений? - PullRequest
0 голосов
/ 04 июня 2011

Допустим, у вас есть кусок кода вроде:

resource = allocateResource();
try { /* dangerous code here  */ }
finally { free(resource); }

Я не имею в виду какой-либо конкретный язык здесь, но я думаю, что Java, C # и C ++ были бы хорошими примерами (если вы используете __try / __finally в MSVC ++).

Это исключение безопасно?

Лично я не думаю, что это безопасно для исключений, потому что, если есть исключение до , когда вы входите в блок try? Тогда ваш ресурс утечет.

Я видел это достаточно много раз, хотя, я думаю, что я что-то упускаю ... я? Или это действительно небезопасно?


Edit:

Я не спрашиваю о allocateResource выдаче исключения, но ситуация, в которой вы получаете исключение после , что функция вернулась, но до resource назначено.

Ответы [ 7 ]

6 голосов
/ 04 июня 2011

Я не спрашиваю о предоставлении allocateResource исключения, а о ситуации в котором вы получаете исключение после эта функция вернулась, но до назначения ресурса.

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

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

3 голосов
/ 04 июня 2011

Смысл в том, чтобы весь код, который может генерировать исключение внутри блока try В вашем случае:

try
{
    resource = allocateResource();
    //...
}
finally { free(resource); }

В противном случае - нет, конечно, это не безопасно.

2 голосов
/ 04 июня 2011

В случае C # это считается небезопасным, поскольку между выделением ресурса и началом блока try может быть создано исключение ThreadAbortException.По этой причине C # 4 изменяет расширение блока using, чтобы переместить распределение ресурсов внутри попытки, а блок finally использует скрытый логический (или проверяет нулевое значение - точно не помню), чтобы определитьдействительно ли распределение имело место.

1 голос
/ 04 июня 2011

Зависит от того, как пишется allocateResource. Учитывая приведенный выше фрагмент, allocateResource может привести к двум результатам: 1) Распределяет и возвращает ресурс 2) Исключает (и поэтому не возвращает ресурс)

Таким образом, если allocateResource обязательно не утечет какие-либо внутренние распределения перед броском, вышеприведенный не утечет resource, так как этот метод не может и бросить, и вернуться.

0 голосов
/ 04 июня 2011

В C # любой управляемый ресурс, на который нет ссылок, будет собираться мусором при следующем запуске, поэтому у вас нет проблем.

0 голосов
/ 04 июня 2011

Я думаю, что вы отвечаете на свой вопрос.Если allocateResource выделяет, а затем выдает исключение до того, как ресурс назначен переменной, то ресурс утечет, и блок try/finally ничего не может с этим поделать, потому что блоки try/finally в большинстве языков (включая Java и C #) делаютна самом деле не знает о ресурсах, которые вы используете внутри них.

Итак, чтобы try/finally был эффективным allocateResource должен каким-то образом гарантировать быть атомарным;либо выделить и присвоить переменной без сбоев, либо вообще не выделить и сбоить.Поскольку такой гарантии нет (особенно учитывая непредсказуемую гибель потоков), блоки try/finally не могут быть эффективно безопасными.

Некоторые языки начинают поддерживать предложения using или with, которые знают о ресурсе и, следовательно, могут безопасно их закрывать (хотя это будет зависеть от реализации интерпретатора / компилятора / среды выполнения и т. д.)

0 голосов
/ 04 июня 2011

Безопасен только код внутри блока try {}. И только если все исключения перехвачены правильно.

Конечно, кода вне блока не будет, и это именно желаемое поведение.

Обратите внимание, что код в блоке finally {} также может генерировать исключения, поэтому вам может потребоваться включить блоки try-catch внутри блоков finally или catch.

например:.

try {
    // your code here
} finally {
    try {
        // if the code in finally can throw another exception, you need to catch it inside it
    } catch (Exception e) {
       // probably not much to do besides telling why it failed
    }
} catch (Exception e) {
    try {
        // your error handling routine here
    } catch (Exception e) {
       // probably not much to do besides telling why it failed
    }
}
  • Если исключение выдается перед выделением, то освобождать нечего и, следовательно, нечего просачиваться.
  • Если исключение возникает после выделения и внутри блока try / catch, то оно будет обработано окончательно
  • если исключение может произойти после выделения и до блока try / catch, то код следует пересмотреть и эти проблемные строки следует переместить в блок.
...