Является ли 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, то код должен быть пересмотрен, и эти проблемные строки должны быть перемещены внутри блока.
Другие вопросы по тегам