在C#winform下载完成的事件后,DownloadFileCompleted不会立即生效?

时间:2017-11-03 11:33:33

标签: c# winforms webclient downloadfileasync

开发环境:

C#,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事件?

2 个答案:

答案 0 :(得分:0)

我建议使用类似其他问题“How to implement a Timeout on 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);

答案 1 :(得分:0)

我认为问题是DownloadFileCompleted事件与表单的Load事件放在同一事件队列中,因此在Form1_Load完成之前无法处理。

通常,阻止UI线程(如在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();
}