Вопрос по c#, blocking, winforms – C #: блокировка вызова функции, пока не выполнено условие

12

Я разрабатываю приложение C # Winforms, часть приложения будет загружать файлы на веб-сервер, используя AsyncUpload (используя его, из-за необходимости использовать обратный вызов porgress), В программе C #

у меня есть простой цикл, который вызывает функцию загрузки

 for(int i=0;i < 10 ; i++)
{
  Uploadfun();
}

И веселье делает немного магии:

Uploadfun()
  { 
  // Logic comes here

   // webClient.UploadFileAsync runs a 2nd thread to perform upload .. 
   webClient.UploadFileAsync(uri, "PUT", fileNameOnHD);  

 }

И обратный вызов, который вызывается после завершения асинхронной загрузки.

Upload_Completed_callback()
{
  //Callback event
}

редактировать

Логическая последовательность:

Веселье вызывается (из цикла)Веселая логика выполнена и выполнена ..Возвращается к петлеОбратный вызов будет вызван в конце концов, когда UploadFileAsync (который выполняет некоторую логику в другом потоке) завершится

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

(F # asyncs FTW!) Brian
У вас есть доступ кfun реализация? Возможно, вам следует рассмотреть возможность предоставления синхронного интерфейса, в котором реализован асинхронный API. Mehrdad Afshari
@Madi: Тогда ты делаешь это задом наперед. Вы должны использовать синхронную версиюUploadFile в ядре логики и используйте асинхронный API поверх него, если вам это нужно. Mehrdad Afshari
@Mehrdad: я фактически вынужден использовать версию Async, потому что она предоставляет мне обратные вызовы «progress», что является обязательным требованием. Madi D.
Как вы описываете, ваш код выглядит последовательным и однопоточным, что означает, что доcallback Функция называется цикл не будет продолжаться. Пожалуйста, поправьте меня, если я ошибаюсь, дав более подробное объяснение. Darin Dimitrov

Ваш Ответ

4   ответа
20

если я правильно понимаю, вы хотите позвонитьUploadFileAsync затем блокируйте, пока асинхронный вызов не достигнет вашего обратного вызова. Если так, я бы использовалAutoResetEvent т.е.

private readonly AutoResetEvent _signal = new AutoResetEvent(false); 

fun()
  { 
  // Logic comes here

   // runs a 2nd thread to perform upload .. calling "callback()" when done
   webClient.UploadFileAsync(uri, "PUT", fileNameOnHD);  

   _signal.WaitOne();   // wait for the async call to complete and hit the callback     
 }



callback()
 {
   //Callback event
   _signal.Set(); // signal that the async upload completed
 }

С помощьюAutoResetEvent означает, что состояние автоматически сбрасывается послеSet был вызван и ожидающий поток получает сигнал черезWaitOne

при использовании Waitone () после uploadAsync, программа останавливается, и обратный вызов не вызывается. Madi D.
хорошо ... я исправил проблему остановки, вызванную fun () в отдельном потоке, "очевидно", вызывая его в основном потоке, вызывает проблему! @Juliet Kinda помогла указать на проблему .. спасибо вам обоим =) Madi D.
3

решение Джульетты работает, поток, выполняющий ожидание вращения, будет потреблять значительное количество процессора пропорционально WaitHandle, который по существу будет простаивать.

+1 пролив свет на оба главных ответа (для меня) .. Madi D.
Он не потребляет много ресурсов процессора, но может помешать системе перевести процессор в состояние более глубокого простоя и, следовательно, быстрее разряжать батарею на ноутбуке. Ben Voigt
1

Проблема здесь:

for(int i=0;i < 10 ; i++)
{
  fun(); <-- if we block until this function finishes here, we stop the UI thread
}

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

volatile downloadComplete;

void DownloadUpdates()
{
    ThreadPool.QueueUserWorkItem(state =>
        for(int i = 0; i < 10; i++)
        {
            downloadComplete = false;
            webClient.UploadFileAsync(uri, "PUT", fileNameOnHD);
            while(!downloadComplete) { Thread.Sleep(1); }
        });
}

Upload_Completed_callback()
{
    downloadComplete = true;
}

Теперь вы можете блокировать выполнение цикла, не прерывая поток пользовательского интерфейса, и вы также получаете преимущество от индикаторов прогресса от класса webclient.

Большое спасибо за действительно полезный ответ, в итоге объединившее потоки из вашего решения с решением resetEvent @ zebrabox, и теперь оно работает .. =) Madi D.
@Juliet, согласно правилам модели памяти .NET, эти два в точности эквивалентны: while (! DownloadComplete) {Thread.Sleep (1); } и if (! downloadComplete) {while (true) {Thread.Sleep (1); }} Поскольку цикл не меняет переменную, компилятор может переместить тест за пределы цикла. Я вижу, что теперь вы добавили синтаксическую ошибку, пытаясь сделать загрузку изменчивой. Если вы исправите объявление, это не даст компилятору выполнить оптимизацию, о которой я упоминал, но он все еще опрашивает, что хуже, чем событие по ряду причин. Ben Voigt
@Juliet: здесь очень информативное обсуждение в контексте Java, но этот конкретный случай идентичен в C # (поскольку оптимизация допустима при последовательной согласованности, а модели памяти Java и .NET CLR слабее этого):java.sun.com/docs/books/jls/third_edition/html/memory.html  Посмотрите особенно на раздел 17.9 этой страницы. Ben Voigt
Не используйте для этого логическую переменную, компилятор может оптимизировать ее (что может привести к тому, что поведение никогда не будет продолжаться, как упоминает OP). Zebrabox показал правильный способ сделать это с ожидаемым объектом события. Ben Voigt
4

поэтому вам не нужно ничего делать. Я предполагаю, что по какой-то причине вы вызываете неблокирующий метод, который запускает фоновую задачу / поток / что угодно и дает вам обратный вызов, когда это будет сделано. Вы хотите вызвать этот асинхронный метод синхронно.

Ты можешь позвонитьfun изнутри обратного вызова. Что-то вроде этого (псевдокод):

int n;

callFunTenTimes()
{
    n = 0;
    fun(n);
}

callback()
{
    ++n;
    if (n < 10)
       fun(n);
    else
       print("done");
}

Это похоже напродолжение прохождения стиля.

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

Мне нужна асинхронная версия, потому что она обеспечивает обратный вызов «progress», который является основным требованием. Madi D.
я отредактировал вопрос и добавил ответы на ваши вопросы, а также дополнительную информацию. Madi D.
@Madi D: Если требование заключается в том, что загрузка должна вести себя асинхронно, почему бы не сделать вашу функцию также асинхронной с обратным вызовом? Можете ли вы объяснить больше о том, для чего вы используете это? Это приложение WinForms, ASP.NET и т. Д.? Mark Byers
+1 за то, что научил меня новой концепции и дал альтернативное решение! Madi D.
Забавно. Я думал о своей собственной проблеме в том же духе: заставить функцию обратного вызова делать «мясо» того, что раньше делал цикл. Это решение, кажется, сводит время ожидания к минимуму. Joe

Похожие вопросы