DownloadFileCompleted не работает сразу после завершения загрузки в С# winform?

100
6

среда разработки:

С#, visual studio 2010 (.net 4.0), win7 x64

коды в проекте winform:

private void Form1_Load(object sender, EventArgs e)
{
string path = "c:\\1.jpg";
for (int i = 0; i < 10; i++)
{
string url = "http://...." + i.ToString() + ".jpg";//i'm sure the http file does exist

using (WebClient wc = new WebClient())
{
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(wc_DownloadFileCompleted);
wc.DownloadFileAsync(new Uri(url), path);

Thread.Sleep(3000);//i'm sure the download will be finished in 3s

WriteLog("C:\\1.log", "main function\r\n");

}
}
}

static void wc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
WriteLog("C:\\1.log", "callback function\r\n");
}

static void WriteLog(string LogName, string log)
{
StreamWriter sw = new StreamWriter(LogName, true);

if (sw == null)
return;

sw.Write(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + " " + log);

sw.Close();
}

то журнал будет:

2017/11/03 19:04:48 Основная функция

2017/11/03 19:04:51 главная функция

2017/11/03 19:04:54 главная функция

2017/11/03 19:04:57 Основная функция

2017/11/03 19:05:00 Основная функция

2017/11/03 19:05:03 Основная функция

2017/11/03 19:05:06 Основная функция

2017/11/03 19:05:09 Основная функция

2017/11/03 19:05:12 Основная функция

2017/11/03 19:05:15 Основная функция

2017/11/03 19:05:15 функция обратного вызова

2017/11/03 19:05:15 функция обратного вызова

2017/11/03 19:05:15 функция обратного вызова

2017/11/03 19:05:15 функция обратного вызова

2017/11/03 19:05:15 функция обратного вызова

2017/11/03 19:05:15 функция обратного вызова

2017/11/03 19:05:15 функция обратного вызова

2017/11/03 19:05:15 функция обратного вызова

2017/11/03 19:05:15 функция обратного вызова

2017/11/03 19:05:15 функция обратного вызова

если те же коды в ConsoleApplication:

static void Main(string[] args)
{
string path = "c:\\1.jpg";
for (int i = 0; i < 10; i++)
{
string url = "http://...." + i.ToString() + ".jpg";//i'm sure the http file does exist

using (WebClient wc= new WebClient())
{
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(wc_DownloadFileCompleted);
wc.DownloadFileAsync(new Uri(url), path);

Thread.Sleep(3000);//i'm sure the download will be finished in 3s

WriteLog("C:\\1.log", "main function\r\n");

}
}
}

static void wc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
WriteLog("C:\\1.log", "callback function\r\n");
}

static void WriteLog(string LogName, string log)
{
StreamWriter sw = new StreamWriter(LogName, true);

if (sw == null)
return;

sw.Write(System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + " " + log);

sw.Close();
}

то журнал будет:

2017/11/03 19:13:50 функция обратного вызова

2017/11/03 19:13:52 Основная функция

2017/11/03 19:13:53 функция обратного вызова

2017/11/03 19:13:55 Основная функция

2017/11/03 19:13:56 функция обратного вызова

2017/11/03 19:13:58 Основная функция

2017/11/03 19:13:59 функция обратного вызова

2017/11/03 19:14:01 основная функция

2017/11/03 19:14:02 функция обратного вызова

2017/11/03 19:14:04 основная функция

2017/11/03 19:14:05 функция обратного вызова

2017/11/03 19:14:08 Основная функция

2017/11/03 19:14:08 функция обратного вызова

2017/11/03 19:14:11 Основная функция

2017/11/03 19:14:11 функция обратного вызова

2017/11/03 19:14:14 Основная функция

2017/11/03 19:14:14 функция обратного вызова

2017/11/03 19:14:17 Основная функция

2017/11/03 19:14:17 функция обратного вызова

2017/11/03 19:14:20 Основная функция

Очевидно, что второй результат правильный.

Но в первом проекте почему событие DownloadFileCompleted не вызывается до завершения всех загрузок?

Как вызвать событие DownloadFileCompleted сразу после каждой загрузки?

спросил(а) 2017-11-03T14:33:00+03:00 2 года, 8 месяцев назад
1
Решение
108

Я считаю, что проблема заключается в том, что событие DownloadFileCompleted помещается в ту же очередь событий, что и событие Load формы, поэтому его нельзя обработать до Form1_Load пор, пока Form1_Load завершится Form1_Load.

В общем, блокирование потока пользовательского интерфейса (например, спать в Form1_Load) является плохой практикой. Длительный код должен выполняться в фоновом потоке. Ниже приведен один из способов:

private void Form1_Load(object sender, EventArgs e)
{
new Thread(() =>
{
string path = "c:\\1.jpg";
for (int i = 0; i < 10; i++)
{
string url = "http://...." + i.ToString() + ".jpg";//i'm sure the http file does exist

using (WebClient wc = new WebClient())
{
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(wc_DownloadFileCompleted);
wc.DownloadFileAsync(new Uri(url), path);

Thread.Sleep(3000);//i'm sure the download will be finished in 3s

WriteLog("C:\\1.log", "main function\r\n");

}
}
}).Start();
}

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

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

private void Form1_Load(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (sender_, e_) =>
{
string path = "c:\\1.jpg";
for (int i = 0; i < 10; i++)
{
string url = "http://...." + i.ToString() + ".jpg";//i'm sure the http file does exist

using (WebClient wc = new WebClient())
{
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(wc_DownloadFileCompleted);
wc.DownloadFileAsync(new Uri(url), path);

Thread.Sleep(3000);//i'm sure the download will be finished in 3s

WriteLog("C:\\1.log", "main function\r\n");

}
}
};
worker.RunWorkerAsync();
}

ответил(а) 2017-11-03T15:30:00+03:00 2 года, 8 месяцев назад
39

Я рекомендую использовать такой подход, как из этого другого вопроса " Как реализовать Timeout на WebClient.DownloadFileAsync "

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


        CancellationTokenSource source = new CancellationTokenSource();
source.CancelAfter(TimeSpan.FromSeconds(5));

await Task.Factory.StartNew(() =>
{
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(wc_DownloadFileCompleted);
wc.DownloadFile(new Uri("MyInternetFile"), filePath);

}, source.Token);

ответил(а) 2017-11-03T15:00:00+03:00 2 года, 8 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема