繁体   English   中英

使用WebClient下载文件时UI冻结

[英]UI freezes while File-Download with WebClient

我正在尝试使用WebClient.DownloadFileAsync-Method下载一些文件。 只要没有显示UI,它就可以正常工作。

UI是一个带有标签和ProgressBar的窗体。

在DownloadProgressChanged-Event中,我想显示当前进度。 为了做到这一点,我调用了一个带有int参数的方法。 这些是下载方法:

private void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    if(progressDialog!=null){
        progressDialog.setFileProgress(e.ProgressPercentage);
    }
    Trace.WriteLine(String.Format("downloaded {0} of {1} bytes. {2} % complete...", 
        e.BytesReceived, 
        e.TotalBytesToReceive,
        e.ProgressPercentage));
}


private void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{               
    if(progressDialog!=null){               
        progressDialog.setFileProgress(100);
    }
    are.Set();
}


private AutoResetEvent are = new AutoResetEvent(false);
public void DownloadFiles(List<DownloadObject> objects){
    Trace.WriteLine("Start Download");
    wc.DownloadProgressChanged += DownloadProgressChanged;
    wc.DownloadFileCompleted += DownloadFileCompleted;
    try{ 
        foreach(DownloadObject dlo in objects){                 
            currentFile = dlo;
            String url = dlo.DownloadURL;
            String path = dlo.LocalPath;
            Uri uri = new Uri(url);
            //GET
            Thread thread = new Thread(() => wc.DownloadFileAsync(uri,path));
            //thread.SetApartmentState(ApartmentState.STA);
            thread.Start();                 
            are.WaitOne();
            DeleteFile(dlo.ID);
        }               
        Trace.WriteLine("FileDownload finished");
    }catch(Exception ex){
        Trace.WriteLine("FileDownload failed: "+ex.Message);
    }finally{
        wc.Dispose();   
    }           
}

这些是ProgressDialog-Form内的相关方法:

public delegate void dummy();
public void setFileProgress(int progress){          
    if(prgFile.InvokeRequired){
        Trace.WriteLine("Invoke required");
        prgFile.Invoke(new dummy(() => prgFile.Value = progress));
    }else{              
        Trace.WriteLine("Invoke not required");             
        prgFile.Value = progress;
    }           
}

public static ProgressDialog getInstance(IWin32Window owner){
    ProgressDialog pd = new ProgressDialog();
    //pd.Show(owner); //
    return pd;
}

现在发生的事情是:如果不调用pd.Show(),一切都很好。 进度被更改,我得到输出“不需要调用”以及下载的每个步骤。

但是,如果调用了pd.Show(),我将多次获得“需要调用”输出,而没有之间的任何下载消息。

因此,我调试了该部分代码,似乎可以调用progressDialog.setFileProgress(),但在调用prgFile.Invoke-method之后,立即再次触发DownloadProgressChanged-Event。

如果我将Invoke调用切换为BeginInvoke,则我会再次获得所有正确的消息,但是ProgressDialog会冻结,直到所有下载完成并且我没有看到任何进度。

我在那里想念什么? 我通读了很多有关此问题和线索,但无法使其正常运行。

我正在将SharpDevelop与.Net-Framework 4.0一起使用

由于您的“ DownloadFiles”方法是同步的,因此会阻塞UI线程。 您启动一个新线程来下载文件,但是

are.WaitOne();

阻止当前线程(UI线程)直到are设置(在download complete事件中)。

正确的解决方案是将完成下载后的工作(例如DeleteFile)放在工作中。

因为DownloadFiles方法是同步的,并且正在UI线程中运行。 因此,如果执行该方法,则在该方法完成之前,您的UI线程将被阻塞。

有两种解决方法:

多线程

只需让Method在其他任务中运行即可解决UI冻结问题。 但是,正如您提到的,在您的情况下,这将是一个可怕的方法,因为您无法操纵UI跨线程。 如果您要进行状态更新,则不应该这样做。

异步/等待

更好的方法在这里。 尽管看似非常简单,但异步/等待实际上很难做到正确。 互联网上有很多资源,我建议从dotnetperls开始,这是对async / await的详细介绍。 这将需要一些时间,但绝对是解决此问题的最佳方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM