Нужно ли прикреплять анонимного делегата? - PullRequest
7 голосов
/ 29 марта 2011

Я вызываю CopyFileEx из приложения C # с анонимным делегатом, передаваемым в параметр LPPROGRESS_ROUTINE, чтобы получать уведомления о ходе копирования файла.

У меня вопрос: нужно ли закреплять анонимного делегата и почему (или почему нет).

Кроме того, меняется ли ответ, если:

  1. CopyFileEx не блокировался.
  2. Если я передал делегата, который не был анонимным.

Спасибо!

Ответы [ 3 ]

6 голосов
/ 29 марта 2011

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

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

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

Другими словами, если вы говорите:

void M()
{
    Foo foo = GetAFoo();
    UnmanagedLibrary.DoSomethingToFoo(foo);
}

, тогда дрожаниеразрешено говорить «вы знаете, я вижу, что ни один управляемый код никогда не использует foo снова в тот момент, после того, как неуправляемый вызов вызван; поэтому я могу настойчиво вернуть хранилище этого объекта из другого потока в то время».Это означает, что неуправляемый вызов может работать с объектом, когда он внезапно освобождается в другом потоке.

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

В этом случае вам необходимо использоватьKeepAlive, чтобы поддерживать управляемый объект. Не полагайтесь на локальную переменную;Локальные переменные специально задокументированы как , а не , гарантированно поддерживающие жизнь.

Подробнее см. http://msdn.microsoft.com/en-us/library/system.gc.keepalive.aspx.

3 голосов
/ 29 марта 2011

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

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

2 голосов
/ 29 марта 2011

Из следующего msdn кажется, что в этом случае и пиннинг, и GC.KeepAlive не нужны, поскольку CopyFileEx является синхронным.В частности, он говорит:

"Обычно вам не нужно беспокоиться о времени жизни делегатов. Всякий раз, когда вы передаете делегат в неуправляемый код, CLR будет следить за тем, чтобы делегат был жив во время вызова. Однако, если собственный код хранит копию указателя за пределами диапазона вызова и намеревается выполнить обратный вызов через этот указатель позже, вам может понадобиться использовать GCHandle, чтобы явно запретить сборщику мусора делегат. "

Поскольку CopyFileEx не держит указатель на функцию за пределами диапазона вызова, нам не нужно вызывать KeepAlive.

...