Является ли Try/ Наконец безопасным для исключений?
Допустим, у вас есть кусок кода вроде:
resource = allocateResource();
try { /* dangerous code here */ }
finally { free(resource); }
Я не имею в виду какой-либо конкретный язык, но я думаю, что Java, C# и C++ были бы хорошими примерами (если вы используете __try
/ __finally
в MSVC++).
Это исключение безопасно?
Лично я не думаю, что это безопасно для исключений, потому что, если есть исключение, прежде чем войти в try
блок? Тогда ваш ресурс будет течь.
Я видел это достаточно много раз, хотя, я думаю, что я что-то упустил... я? Или это действительно небезопасно?
Редактировать:
Я не спрашиваю о allocateResource
выдает исключение, но ситуация, в которой вы получаете исключение после того, как эта функция вернулась, но до resource
назначен.
7 ответов
Я не спрашиваю о вызове allocateResource исключения, а о ситуации, в которой вы получаете исключение после того, как эта функция вернулась, но до назначения ресурса.
Попытка справиться с этим аспектом безопасности исключений становится очень грязной, не в последнюю очередь потому, что языковые конструкции не позволяют вам устанавливать обработчик finally в середине оператора присваивания.
Мое обоснование всего этого заключается в том, что если вы не можете от конца вызова функции получить назначение переменной, то ваша система уже подключена. Кого волнует, если вы теряете память, когда не можете присвоить переменную?
Дело в том, чтобы иметь весь код, который может генерировать исключения внутри try
блок. В твоем случае:
try
{
resource = allocateResource();
//...
}
finally { free(resource); }
В противном случае - нет, конечно, это не безопасно.
В случае C# это считается небезопасным, поскольку между распределением ресурса и началом блока try может быть создано исключение ThreadAbortException. По этой причине C#4 меняет расширение using
блок для перемещения выделения ресурсов внутри попытки, и finally
Блок использует скрытое логическое значение (или проверяет нулевое значение - я точно не помню), чтобы определить, действительно ли произошло распределение.
Это зависит от того, как пишется allocateResource. С учетом приведенного выше фрагмента allocateResource может привести к двум результатам: 1) Распределяет и возвращает ресурс. 2) Исключает (и, следовательно, не возвращает ресурс).
Так что если allocateResource
Перед выбрасыванием обязательно не произойдет утечки изнутри, вышеописанное не утечет resource
так как этот метод не может и бросить, и вернуться.
Я думаю, что вы отвечаете на свой вопрос. Если allocateResource
выделяет, а затем выдает исключение, прежде чем ресурс будет назначен переменной, тогда ресурс утечет, и нет ничего try/finally
Блок может с этим поделать, потому что try/finally
блоки в большинстве языков (включая Java и C#) фактически не знают о ресурсах, которые вы используете внутри них.
Итак, для того, чтобы try/finally
чтобы быть эффективным allocateResource
должен как-то гарантировать атомность; либо выделить и присвоить переменной без сбоев, либо вообще не выделить и сбоить. Поскольку такой гарантии нет (особенно учитывая непредсказуемую гибель потоков), то try/finally
блоки не могут быть эффективно безопасными.
Некоторые языки начинают поддерживать using
или же with
предложения, которые знают о ресурсе и, следовательно, могут безопасно их закрывать (хотя это будет зависеть от реализации интерпретатора / компилятора / среды выполнения и т. д.)
В C# любой управляемый ресурс, на который нет ссылок, будет собираться мусором при следующем запуске, поэтому у вас нет проблем.
Только код внутри блока 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, то код должен быть пересмотрен, и эти проблемные строки должны быть перемещены внутри блока.