Finalizer выдает случайные исключения, вызывает случайные ошибки, зависает приложение

У меня есть класс на C++/CLI, который использует неуправляемые ресурсы (HANDLE для собственного потока (т.е. из CreateThread()) и LPVOID для волокна из CreateFiber/ConvertThreadToFiber).

По совету, который я получил от MSDN, я очищаю неуправляемые ресурсы в финализаторе (!Fiber()), а деструктор (~Fiber()) вызывает финализатор.

Вот код:

Fiber::~Fiber () {

    this->!Fiber();

}


Fiber::!Fiber () {

    if (thread!=NULL) {

        delete thread;
        thread=NULL;

    }

    if (fiber!=NULL) {

        DeleteFiber(fiber);
        fiber=NULL;

    }

}

У меня есть тестовое приложение, которое создает два волокна, тестирует их, а затем утилизирует по мере их использования. Первый утилизируется нормально. Последний расположен как последняя строка программы, и он вылетает одним из трех разных способов:

Unhandled Exception: System.AccessViolationException: Attempted to read or write
 protected memory. This is often an indication that other memory is corrupt.
   at DeleteFiber(Void* )
   at System.Threading.Fiber.!Fiber()
   at System.Threading.Fiber.Dispose(Boolean )
   at System.Threading.Fiber.Finalize()

Эта ошибка также может исходить из строки:

delete thread;

Также.

Он также может вылететь с OutOfMemoryException или зависнуть на некоторое время, говоря, что программа испытала переполнение стека, а затем зависнуть консоль (мне нужно закрыть cmd.exe и перезапустить его для восстановления).

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


person Robert Allan Hennigan Leahy    schedule 29.12.2011    source источник
comment
Как выглядит метод DeleteFiber()?   -  person Corith Malin    schedule 29.12.2011
comment
Вот так.   -  person Robert Allan Hennigan Leahy    schedule 29.12.2011


Ответы (1)


  1. Если thread является HANDLE, вы очищаете его с помощью CloseHandle(thread), а не delete thread.
  2. Вы должны инициализировать thread и fiber до NULL в конструкторе Fiber, чтобы сохранить инварианты класса.
  3. Вы не можете вызвать DeleteFiber на выполняющемся в данный момент волокне, если только вы не хотите завершить поток. Вы очищаете его, вызывая ConvertFiberToThread()
person MSN    schedule 29.12.2011
comment
Интересно. Однако это не решает проблему — я все еще получаю исключения AccessViolationException от DeleteFiber. - person Robert Allan Hennigan Leahy; 29.12.2011
comment
Конструктор делает обе вещи, которые вы упомянули в № 2. Я вызываю Dispose() из другого файбера (в первом случае), а затем из метода Main() программы на C# (который находится в другом потоке, во втором случае). - person Robert Allan Hennigan Leahy; 29.12.2011
comment
№ 3 было косвенным решением. Попытка очистить волокно, сделанное из нити, путем очистки нити сначала делает волокно недействительным. Решение состояло в том, чтобы изменить код так, чтобы DeleteFiber выполнялся только в том случае, если thread равно NULL. - person Robert Allan Hennigan Leahy; 29.12.2011