【问题标题】:Prevent form from freezing防止表格冻结
【发布时间】:2009-08-24 13:26:09
【问题描述】:

我想下载一张图片,然后在图片框中显示。

一开始我是这样的:

WebClient client = new WebClient();
client.DownloadFile(url, localFile);
pictureBox2.Picture = localFile;

但这并不完美,因为在执行下载期间,应用程序有点卡住了。

然后我改成这样:

public class ParamForDownload
{
    public string Url { get; set; }
    public string LocalFile { get; set; }
}
ParamForDownload param = new ParamForDownload()
        {
            Url = url,
            LocalFile = localFile
        };

      ThreadStart starter = delegate { DownloadMap (param); };
        new Thread(starter).Start();

        pictureBox2.Picture = localFile;



    private static void DownloadMap(ParamForDownload p) 
    {
        WebClient client = new WebClient();
       client.DownloadFile(p.Url, p.LocalFile);
    }

但现在我必须执行“等待线程结束”之类的操作,因为文件是在线程中访问的,并且同时通过 DownloadMap 方法将某些内容下载到文件中。

解决该问题的最佳等待时间是什么?

【问题讨论】:

    标签: c# .net winforms multithreading


    【解决方案1】:

    基本上,最初发生的事情是 UI 线程正在完成下载,因为它正在为此工作,所以无法刷新或绘制(同步工作)。现在发生的事情是您正在启动线程,然后 UI 线程正在继续,然后尝试将本地文件(尚未完成下载)分配给图片框。您应该尝试以下任一方法:

    您应该使用background worker 来完成您的下载任务。

    它有非常方便的事件。 DoWork,您可以在其中开始下载。

    还有一个 RunWorkerCompleted 事件在工作完成时触发。您可以在哪里设置图像 (pictureBox2.Picture = localFile;)。

    绝对值得一试,我认为这是完成您想要实现的目标的最合适方式。

    或者

    如果您想坚持使用线程。完成Thread.Start() 后,您可以取出图像分配,并将其放入您的工作线程函数中:

    private delegate void MyFunctionCaller();
    
    private static void DownloadMap(ParamForDownload p) 
    {
        WebClient client = new WebClient();
       client.DownloadFile(p.Url, p.LocalFile);
        DownloadMapComplete(p);
    }
    
    private void DownloadMapComplete(ParamForDownload p)
    {
    if (InvokeRequired == true)
      {
      MyFunctionCaller InvokeCall = delegate { DownloadMapComplete(p); };
      Invoke(InvokeCall);
      }
    else
      {
      pictureBox2.Picture = p.LocalFile;
      }
    }
    

    【讨论】:

    • BackgroundWorker 绝对是您正在搜索的东西。我刚刚使用它,它很容易使用。只需在 DoWork 事件中添加一些内容,将结果放入 e.Result 并在 RunWorkerCompleted 事件中使用此值。或者,如果您的长时间运行的函数更复杂,请在 ((MethodInvoker)e.Argument).Invoke(); 中调用它们。并通过 BeginInvoke 在您长期运行的函数中更新您的 GUI 元素。要从 GUI 线程确定是否有任何事情发生,您可以检查 backgroundWorker.IsBusy;
    • 欲了解更多信息,请查看:msdn.microsoft.com/en-us/library/…
    • @Oliver:是的,他们对开发人员非常友好,他们为我们做了很多工作。
    【解决方案2】:

    最简单的解决方案是只使用Picturebox.LoadAsync() 方法并让Picturebox 担心在后台加载它。如果您需要检查错误,请使用(图片框的)LoadCompleted 事件。

    这样的一行:

    pictureBox1.LoadAsync(@"http://imgs.xkcd.com/comics/tech_support_cheat_sheet.png");
    

    就是你所需要的。

    【讨论】:

      【解决方案3】:

      当用户启动流程时,您需要做的是:

      1) 更新 UI 以指示正在发生的事情。 IE:禁用所有字段并显示“我正在下载文件,请稍候...”的消息。优先使用某种进度指示器(抱歉,我不确定 WebClient 是否支持进度等,但您需要更新 UI,因为下载需要一段时间)。

      2) 将事件处理程序附加到 WebClient 的“DownloadFileCompleted”。

      3) 使用 WebClient 的 DownloadFileAsync 方法在另一个线程上开始下载。您不需要自己旋转线程。

      4) 当 'DownloadFileCompleted' 事件被触发时,使用表单的调用方法将调用路由回 UI 线程。这很重要。

      见:http://weblogs.asp.net/justin_rogers/pages/126345.aspx

      和:http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx

      5) 一旦事件被路由回 UI 线程,打开文件并根据需要更新 UI(IE:重新启用字段等)。

      皮革。

      【讨论】:

      • 是的,WebClient 有一个 DownloadProgressChanged 和 DownloadCompleted 事件
      【解决方案4】:

      与线程问题无关,但如果您对将照片保存到磁盘没有任何其他要求,您可以这样做:

      WebClient client = new WebClient();
      byte[] data = client.DownloadData(item.Url);
      MemoryStream ms = new MemoryStream(data);
      Bitmap img = new Bitmap(ms);
      pictureBox.Image = img;
      

      【讨论】:

      • 不会让您的磁盘因不需要的文件而杂乱无章。特别是在文件很大的情况下。
      • 这仅在您不会一遍又一遍地下载同一张照片时才有用,在这种情况下,将它们保存到磁盘并在再次下载之前检查它们是否存在会更高效。
      猜你喜欢
      • 1970-01-01
      • 2015-08-16
      • 1970-01-01
      • 1970-01-01
      • 2013-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多