2015-11-15 1 views
22

Примечания: Я использую C# в Unity, это означает, что версии .NET 3.5 , поэтому я не могу использовать await или async ключевое слово ..Асинхронные методы в использовании заявление

Что будет используя заявление, когда Я положил в него метод, который работает асинхронно?

using (WebClient wc = new WebClient()) { 
    wc.DownloadFileAsync(urlUri, outputFile); 
} 
SomeMethod1(); 
SomeMethod2(); 

Как вы знаете, после того, как метод DownloadFileAsync() называется, SomeMethod1() будет называться что из using блока, а DownloadFileAsync() еще работает. Итак, теперь я действительно запутался в том, что произойдет с оператором using и асинхронным методом в этом случае.

Может ли Dispose() из wc быть вызванным в нужное время без проблем?

Если нет, то как мне исправить этот пример?

+3

Это не сработает. Используйте 'await' с' * TaskAsync' – SLaks

+1

@JasonEvans, почему вы говорите «да»? поскольку OP не использует 'await', тогда возникает проблема. –

+0

Большое спасибо за ответы, я сделал это глупые ошибки несколько раз. Тогда как мне избежать этого? Просто добавьте ключевое слово ожидания? – Jenix

ответ

21

Из комментариев:

Тогда как же избежать этого? Просто добавьте ключевое слово ожидания?

Нет, вы не можете всего сделать это. (И поэтому ранее предложенный дублированный вопрос на самом деле не был дубликат и hellip, ваш сценарий тонко отличается.) Вам нужно будет отложить удаление до завершения загрузки, но это осложняется вашей необходимостью выполнить еще два программных оператора (по крайней мере, & hellip, невозможно точно знать без a good, minimal, complete code example).

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

Вы можете обратиться в другой часть проблемы, захватив возвращенный Task объект и не не ожидая, пока после ваших других операторов программы выполнили:

using (WebClient wc = new WebClient()) { 
    Task task = wc.DownloadFileTaskAsync(urlUri, outputFile); 
    SomeMethod1(); 
    SomeMethod2(); 
    await task; 
} 

Таким образом, загрузка может быть запущена , ваши другие два метода вызваны, а тогда код будет ждать завершения загрузки. Только после того, как он будет завершен, будет выведен блок using, позволяющий удалять объект WebClient.

Конечно, в вашей текущей реализации вы, несомненно, занимаетесь соответствующим событием DownloadXXXCompleted. Если вы хотите, вы можете продолжать использовать объект таким образом. Но IMHO, как только вы перешли на использование await, гораздо лучше просто поставить после await код, который необходимо выполнить при завершении операции. Это сохраняет весь код, относящийся к операции, в одном месте и упрощает реализацию.


Если по каким-то причинам вы не можете использовать await, то вам придется использовать некий альтернативный механизм для задержки Избавьтесь от WebClient.Некоторые подходы позволят вам продолжать использовать using, другие потребуют, чтобы вы вызывали Dispose() в обработчик события DownloadXXXCompleted. Без более полного примера кода и четкого объяснения причин, почему await не подходит, было бы невозможно точно сказать, какой будет лучшая альтернатива.


EDIT:

Поскольку вы подтвердили, что у вас нет доступа к await в текущем коде, вот несколько других вариантов, совместимых со старым кодом и hellip;

Одна возможность состоит в том, чтобы просто ждать в том же потоке после начала операции:

using (WebClient wc = new WebClient()) { 
    object waitObject = new object(); 
    lock (waitObject) 
    { 
     wc.DownloadFileCompleted += (sender, e) => 
     { 
      lock (waitObject) Monitor.Pulse(waitObject); 
     }; 
     wc.DownloadFileAsync(urlUri, outputFile); 
     SomeMethod1(); 
     SomeMethod2(); 
     Monitor.Wait(waitObject); 
    } 
} 

(Примечание: можно использовать любую подходящую синхронизацию выше, например, ManualResetEvent, CountdownEvent, или даже Semaphore и/или «тонкие» эквиваленты.Я использую Monitor просто из-за его простоты и эффективности и воспринимаю как предоставленные читатели могут приспособиться к их предпочтительным средствам синхронизации. Одна очевидная причина, которую можно было бы предпочесть другой, чем Monitor, заключается в том, что другие типы синхронизации Techni ques не будет подвергать риску наличие самого блока обработчика событий DownloadFileCompleted, ожидающего завершения методов SomeMethod1() и SomeMethod2(). Независимо от того, важно ли это, зависит от того, как долго будут проходить эти вызовы по методу по сравнению с загрузкой файла.)

Вышеупомянутое, однако, блокирует текущий поток. В некоторых случаях это может быть хорошо, но чаще всего операция запускается в потоке пользовательского интерфейса, и этот поток не должен блокироваться на время операции. В этом случае, вы хотите отказаться от using вообще и просто вызвать Dispose() из обработчика события завершения:

WebClient wc = new WebClient(); 
wc.DownloadFileCompleted += (sender, e) => 
{ 
    wc.Dispose(); 
}; 
wc.DownloadFileAsync(urlUri, outputFile); 
SomeMethod1(); 
SomeMethod2(); 
+0

Я только что узнал, что ключевое слово await не может быть использовано в версии 3.5 Unity3D. Но ваш ответ очень полезен! Спасибо огромное! – Jenix

+1

Очень хорошо. Особенно последнее очень просто и чисто! :) – Jenix

+2

'Monitor.Pulse()' не поддерживает «импульсное состояние», поэтому при выполнении этого пути существует риск блокировки, если обратный вызов завершается до того, как основной поток ожидает (маловероятно, но возможно). «Семафор» мог бы быть лучше в этой ситуации или сдерживать ожидание. –

6

System.Net.WebClient обеспечивает событие DownloadFileCompleted. Вы могли бы добавить обработчик для этого события и избавиться от клиента в это время.

+0

Да! Это один из лучших способов, которые я могу выбрать, спасибо! – Jenix

Смежные вопросы