Дождитесь завершения асинхронной загрузки - PullRequest
0 голосов
/ 26 января 2019

Это весь мой исходный код. Я хочу скачать x файлов (предположим, что у нас теперь есть 2 файла, которые мы хотим загрузить).

Моя главная задача - дождаться завершения всех загрузок и продолжить. Я попробовал несколько пунктов.

Task.WhenAll (Список) -> Не работает в моем способе реализации

await -> невозможно в функции MainWindow без асинхронности.

Рабочий процесс должен быть следующим. Загрузка началась -> Все загруженные файлы -> Показать отладочную печать.

Atm: Исходный код выводится -> Все загрузки сделаны "Загрузка завершена" «Загрузка завершена»

Должно быть: "Загрузка завершена" "Загрузка завершена" Все загрузки сделаны

Работа в течение нескольких часов и без малейшего шага для решения проблемы. Это приложение WPF.

    private volatile bool _download_completed;
    public bool DownloadCompleted { get { return _download_completed; } }


    public MainWindow()
    {

        InitializeComponent();
        ProcessService.FillProcessData();
        ProcessService.CloseAllProcesses();
        DownloadHelper.AddDownloadFiles();

        foreach(KeyValuePair<string,Uri> file in DownloadHelper.DownloadFiles)
        {
            DownloadFile(file.Key, file.Value);
        }
        Debug.Print("All Downloads done.");

    }

  private void DownloadFile(string Filename, Uri Uri)
    {

        Console.WriteLine(Filename + " " + Uri);
        _download_completed = false;
        WebClient client = new WebClient();
        client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadFileCompleted);
        client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressCallback);
        client.DownloadFileTaskAsync(Uri, Filename);

    }

private void DownloadProgressCallback(object sender, DownloadProgressChangedEventArgs e)
    {
        this.Dispatcher.Invoke(() => {

            progressBar.Value = e.ProgressPercentage;
            StatusLabel.Content = e.ProgressPercentage + " % complete... ( " + e.BytesReceived + " / " + e.TotalBytesToReceive + ")";
        });
    }

    private void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
    {
        Console.WriteLine("Download completed");
        this.Dispatcher.Invoke(() =>
        {
            StatusLabel.Content = "Download Finished";
        });

        _download_completed = true;

    }


}

Ответы [ 3 ]

0 голосов
/ 26 января 2019

Обратите внимание, что нижеприведенная загрузка будет загружать полную загрузку в память, если загружаемые файлы очень велики, попробуйте переписать, чтобы вместо этого вызвать GetStreamAsync для HttpClient и выполнить потоковую передачу в файл, но это подойдет для разумных размеров.

Как правило, вы хотите повторно использовать HttpClient, но утилизировать по окончании, поэтому, если это WPF, вы можете создавать и сохранять при запуске и утилизировать в конце, но это, вероятно, также разумно.

Редактировать: Обратите внимание, что вы можете захотеть сделать это в другом потоке и отправить его обратно в случае, если при этом возникает проблема с блокировкой потока пользовательского интерфейса.

void Main()
{
    using (var client = new HttpClient())
    {
        var tasks = new List<Task>();
        var files = new Dictionary<string, string>();
        files.Add("c:\\temp\\MyFilename1.md", "https://raw.githubusercontent.com/aspnet/AspNetCore/master/README.md");
        files.Add("c:\\temp\\MyFilename2.md", "https://raw.githubusercontent.com/aspnet/AspNetCore/master/README.md");

        foreach (var file in files)
        {
            tasks.Add(DownloadFileAsync(client, file.Key, file.Value));
        }

        Task.WaitAll(tasks.ToArray());
    }
}

async Task DownloadFileAsync(HttpClient client, string filename, string url)
{
    var contents = await client.GetStringAsync(url);
    File.WriteAllText(filename, contents);
}
0 голосов
/ 26 января 2019

Моя главная задача - дождаться завершения всех загрузок и затем продолжить.

Я призываю вас сделать шаг назад и рассмотреть UX-дизайн вашего приложения. никогда хорошая идея - ждать ввода-вывода и блокировать поток пользовательского интерфейса при этом.

В частности, ваши конструкторы ViewModel никогда не должны блокировать на I /О.Конструкторы ViewModel должны выполняться немедленно и синхронно, чтобы ваш пользовательский интерфейс мог отображаться немедленно и синхронно.

Теперь конструктор может запускать асинхронные операции;Все в порядке.Это просто не должно ждать их завершения.Ваш конструктор должен инициализировать ViewModel в состояние с надписью «Загрузка ...» или что-то в этом роде, и после завершения загрузки (или по мере их выполнения) ViewModel может обновить свое состояние, чтобы отразить это.

Подробнее об этом шаблоне см. В моей статье MSDN о привязке данных к асинхронным операциям .

0 голосов
/ 26 января 2019

Вам нужно использовать задачи для достижения этой цели

Сначала сделайте вашу функцию загрузки асинхронной

    private async TaskDownloadFile(string Filename, Uri Uri)
        {

            Console.WriteLine(Filename + " " + Uri);
            _download_completed = false;
            WebClient client = new WebClient();
            client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadFileCompleted);
            client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressCallback);

      await   client.DownloadFileTaskAsync(Uri, Filename);

        }

Сделайте метод асинхронным и дождитесь метода DownloadFileTaskAsync.

Я быпредлагаем удалить события для DownloadFileCompeted и DownloadProgressChanged, если вы будете делать несколько загрузок, они не имеют особого смысла

Теперь, когда вы сделали метод async, выполните следующие действия:

var file1DownloadTask = TaskDownloadFile("filename","url");
var file2DownloadTask = TaskDownloadFile("filename","url");

укажите, что у вас есть две задачи

Вы можете использовать Task.WaitAll (list_of_taks_goes_here)

Task.WaitAll(file1DownloadTask,file2DownloadTask )

Метод wait all будет ожидать завершения всех задач, поэтому вызывается следующая строка кодапосле того, как эти две задачи будут завершены

, вы можете изменить пользовательский интерфейс или выполнить все, что вам нужно после строки Task.WaitAll

Вы также можете сделать следующее

await TaskDownloadFile("filename","url");
await TaskDownloadFile("filename2","url");

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...