2015-01-16 5 views
3

У меня есть диалог с индикатором прогресса. Фоновый работник должен загружать два файла (с помощью WebClient) и автоматически копировать их в указанное место, когда отображается диалоговое окно. Как дождаться загрузки файлов перед копированием новых файлов.Ждать загрузки для загрузки в BackgroundWorker

Я попытался что-то сделать с await, но я не могу изменить фонаря на метод async. Как я могу подождать у рабочего для завершения загрузки?

код для запуска работника:

private void fmUpdateingDatabaseDialog_Shown(object sender, EventArgs e) 
{ 
    device.Connect(); 
    lbInformation.Text = "uploading database to " + device.FriendlyName; 
    device.Disconnect(); 

    worker.WorkerReportsProgress = true; 
    worker.WorkerSupportsCancellation = true; 
    worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
    worker.ProgressChanged += 
     new ProgressChangedEventHandler(worker_ProgressChanged); 
    worker.RunWorkerCompleted += 
     new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
    worker.RunWorkerAsync(); 
} 

код в обработчике DoWork (пути не являются пустыми в фактическом коде):

private void worker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    BackgroundWorker worker = sender as BackgroundWorker; 

    //download files temporary 
    WebClient client = new WebClient(); 
    client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged); 
    client.DownloadFileTaskAsync(new Uri(""), Path.Combine(tempPath + "")); 

    WebClient client2 = new WebClient(); 
    client2.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client2_DownloadProgressChanged); 
    client2.DownloadFileTaskAsync(new Uri(""), Path.Combine(tempPath + "")); 

    //upload files to phone 
    device.Connect(); 
     device.TransferContentToDevice(Path.Combine(tempPath+""), folder.Id, folder, true); 
     device.TransferContentToDevice(Path.Combine(tempPath+""), folder.Id, folder, true); 
    device.Disconnect(); 
} 

ответ

1

Вы можете использовать что-то вроде этого, который использует механизм "wait and pulse" для задержки кода до завершения операции загрузки:

var locker = new object(); 

Thread t = new Thread(new ThreadStart(() => 
{ 
    lock (locker) 
    { 
     //peform your downloading operation, and wait for it to finish. 
     client.DownloadFileTaskAsync(new Uri(""), Path.Combine(tempPath + "")); 
     while (/* not yet downloaded */) { }; 
     //inform the parent thread that the download has finished. 
     Monitor.Pulse(locker); 
    } 
})); 

t.Start(); 

lock(locker) 
{ 
    Monitor.Wait(locker); 
} 

Howeve r, если у вас есть ресурсы, я бы предложил рефакторинг вашего кода, чтобы полностью использовать асинхронный подход (таким образом, чтобы избежать фонового работника). Фоновый работник является одним из традиционных асинхронных подходов, тогда как рекомендуемый подход - TAP.

См. Ответ Стивена Клири для примера того, как это сделать.

+3

- ** никогда бы никогда никогда не предлагал Thread.Sleep (любого рода, будь то 0 или 1000) и цикл такого sencario **, скорее пойдите для WaitHandles! (см. мой полный почему/как/... @ http://stackoverflow.com/questions/8815895/why-is-thread-sleep-so-harmful/8815944#8815944) –

+0

Я думал об этом, но это казалось немного грязный для меня. Есть ли у вас ресурсы для подхода async-ожидания? Я не знаю, как запустить асинхронную функцию из события Dialog_shown – j0h4nn3s

+0

@AndreasNiedermair - см. Мое обновление. –

2

Вы можете использовать синхронные WebClient методы (например, DownloadFile вместо DownloadFileTaskAsync), или вы можете просто использовать async/await непосредственно вместо из BackgroundWorker. В этом случае вы делаете главным образом ввод-вывод, поэтому async лучше подходит, чем BackgroundWorker.

async решение будет выглядеть примерно так:

private async void fmUpdateingDatabaseDialog_Shown(object sender, EventArgs e) 
{ 
    device.Connect(); 
    lbInformation.Text = "uploading database to " + device.FriendlyName; 
    device.Disconnect(); 

    var progress = new Progress<T>(data => 
    { 
    // TODO: move worker_ProgressChanged code into here. 
    }); 
    await DownloadAsync(progress); 
    // TODO: move worker_RunWorkerCompleted code here. 
} 

private async Task DownloadAsync(IProgress<T> progress) 
{ 
    //download files temporary 
    WebClient client = new WebClient(); 
    client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged); 
    await client.DownloadFileTaskAsync(new Uri(""), Path.Combine(tempPath + "")); 

    WebClient client2 = new WebClient(); 
    client2.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client2_DownloadProgressChanged); 
    await client2.DownloadFileTaskAsync(new Uri(""), Path.Combine(tempPath + "")); 

    //upload files to phone 
    // TODO: Check for Async versions of these methods that you can await. 
    // If there aren't any, consider using Task.Run. 
    device.Connect(); 
    device.TransferContentToDevice(Path.Combine(tempPath+""), folder.Id, folder, true); 
    device.TransferContentToDevice(Path.Combine(tempPath+""), folder.Id, folder, true); 
    device.Disconnect(); 
} 
+1

Скажите, если я ошибаюсь, но я думал, что могу использовать 'await' только в асинхронных методах. Поскольку 'fmUpdateingDatabaseDialog_Shown' не является async, я не знаю, как это реализовать. Редактировать: Я не синхронизирую WebClient, потому что важно видеть прогресс. – j0h4nn3s

+1

@ j0h4nn3s: Исправлено, спасибо. –

+0

Могу ли я спросить, что такое ваш взгляд на 'async void'? [Я читал статью, что ее следует избегать] (http://haacked.com/archive/2014/11/11/async-void-methods/), и я видел вас здесь, на SO, отвечая на множество задач вопросы :) Я решил, что ваше мнение по этому вопросу может быть очень интересным. – Default

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