【问题标题】:Beginner Thread or Dispatcher direction初学者线程或调度程序方向
【发布时间】:2015-08-18 07:24:10
【问题描述】:

我希望有人能指出我正确的方向。我想做一个简单的 WPF 应用程序,它有一个按钮和一个文本框。我单击按钮,它开始循环下载一堆文件。我似乎无法弄清楚如何不让下载阻止 UI 更新。据我所知,我可能不得不使用一些线程代码;但到目前为止,我发现并尝试过的所有示例都不适合我。关于我应该在哪里寻找和学习的任何帮助或方向都会很棒。我似乎无法弄清楚如何在每个文件下载周围输出那些 textbox.text 消息。

foreach (var ticker in tickers)
    {
        var url = string.Format(urlPrototype, ticker, startMonth, startDay, startYear, finishMonth, finishDay, finishYear, "d");
        var csvfile = directory + "\\" + ticker.ToUpper() + ".csv";

        tbOutput.Text += "Starting Download of : " + ticker + "\n";

        webClient.DownloadFile(url, csvfile);

        tbOutput.Text += "End Download of : " + ticker + "\n";
        numStocks++;
    }
    tbOutput.Text += "Total stocks downloaded = " + numStocks + "\n";

【问题讨论】:

    标签: c# wpf multithreading webclient


    【解决方案1】:

    如果您将方法标记为异步,则可以使用DownloadFileTaskAsync 方法

    await webClient.DownloadFileTaskAsync(url, csvfile)
    

    【讨论】:

    【解决方案2】:

    如果您选择使用BackgroundWorker,它允许您在每次下载文件时将这些消息输出到文本框。这是一个适合您要求的粗略示例。

    1) 在类级别,创建 BackgroundWorker 类的实例,并将事件处理程序添加到 BackgroundWorker 实例的事件中:

    BackgroundWorker workerDownload = new BackgroundWorker();
    workerDownload.WorkerReportsProgress = true;
    workerDownload.DoWork += workerDownload_DoWork;
    workerDownload.ProgressChanged += workerDownload_ProgressChanged;
    workerDownload.RunWorkerCompleted += workerDownload_RunWorkerCompleted;
    

    2) 为后台工作人员的 DoWork 事件创建事件处理程序:

    DoWork 事件处理程序是您运行耗时操作的地方 在后台线程上。传递给后台的任何值 操作在 DoWorkEventArgs 的 Argument 属性中传递 传递给事件处理程序的对象。

    private void workerDownload_DoWork(object sender, DoWorkEventArgs e)
    {
        foreach (var ticker in tickers)
        {
            // you can pass the required info as argument:
            string[] arrArg = (string[])e.Argument;
            string theUrl = arrArg[0];
            string directory = arrArg[1];
    
            var url = string.Format(theUrl, ticker);
            var csvfile = directory + "\\" + ticker.ToUpper() + ".csv";
    
            // perform the download operation and report progress:
            workerDownload.ReportProgress(0, "Starting Download of : " + ticker + "\n");
            webClient.DownloadFile(url, csvfile);
            workerDownload.ReportProgress(100, "End Download of : " + ticker + "\n");
            numStocks++;
        }
    }
    

    3) 为后台工作人员的 ProgressChanged 事件创建事件处理程序:

    在 ProgressChanged 事件处理程序中,添加代码以指示 进度,例如更新用户界面。

    private void workerDownload_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        tbOutput.Text += e.UserState.ToString();
    }
    

    4) 为 RunWorkerCompleted 事件创建事件处理程序:

    RunWorkerCompleted 事件在后台工作人员完成时引发 完成。

    private void workerDownload_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        tbOutput.Text += "Total stocks downloaded = " + numStocks + "\n";
    }
    

    5) 通过调用 RunWorkerAsync 方法开始运行后台操作:

    int numStocks = 0;
    string strDirectory = "<a_directory>";
    string strUrl = string.Format(urlPrototype, startMonth, startDay, startYear, finishMonth, finishDay, finishYear, "d");
    string[] args = new string[2] { strUrl, strDirectory };
    workerDownload.RunWorkerAsync(args);
    

    【讨论】:

      【解决方案3】:

      有很多方法可以实现它。例如:

      1) 如果您在 .Net Framework 4.5 中编程,请使用 async/await。比BackgroundWorker简单

      https://msdn.microsoft.com/en-us/library/hh191443.aspx

      private async void button_Click(object sender, RoutedEventArgs e)
      {
          Uri someUrl=new Uri(@"http://dotnetperls.com");
          WebClient webClient=new WebClient();
          await webClient.DownloadFileTaskAsync(someUrl, csvFile);
      }
      

      2) 背景工作者。此类实际上是为了进行异步操作以避免冻结 UI。 见http://www.wpf-tutorial.com/misc/multi-threading-with-the-backgroundworker

      public partial class MainWindow : Window
      {
          BackgroundWorker bw;
          public MainWindow()
          {
              InitializeComponent();
              bw = new BackgroundWorker();
              bw.DoWork += bw_DoWok;
              bw.RunWorkerCompleted += bw_RunWorkerCompleted;
          }              
      }
      
      
      void bw_RunWorkerComleted(object sender, RunWorkerCompletedEventAgs e)
      {
          MessageBox.Show("The result is " + e.Result.ToString());
      }
      
      void bw_DoWork(object sender, DoWorkEventArgs e)
      {
          foreach (var ticker in tickers)
          {
              var url = string.Format(urlPrototype, ticker, startMonth, startDay, startYear, finishMonth, finishDay, finishYear, "d");
              var csvfile = directory + "\\" + ticker.ToUpper() + ".csv";
              webClient.DownloadFile(url, csvfile);    
              numStocks++;
           }
          e.Result = "End Of Download ";
      }
      
      private void button_Click(object sender, RoutedEventArgs e)
      {
          bw.RunWorkerAsync();
          tbOutput.Text += "Starting Download of : " + ticker + "\n";
      }
      

      3) 使用 Thread 类并使用 Dispatcher 类进行更新:

      ThreadStart job = new ThreadStart(() =>
      {
          foreach (var ticker in tickers)
          {
             var url = string.Format(urlPrototype, ticker, startMonth, startDay, startYear, finishMonth, finishDay, finishYear, "d");
             var csvfile = directory + "\\" + ticker.ToUpper() + ".csv";
             webClient.DownloadFile(url, csvfile);    
             numStocks++;
          }
          Dispatcher.BeginInvoke((Action)(()=> tbOutput.Text += "End Download of : " + ticker + "\n";}));
      
      });
      
      Thread thread = new Thread(job);
      thread.Start();
      

      http://www.beingdeveloper.com/use-dispatcher-in-wpf-to-build-responsive-applications

      【讨论】:

      • 尝试添加一些示例而不是仅仅链接 - 链接是脆弱的。它们非常适合提供更多信息,但如果链接停止工作,您的答案应该可以独立使用。
      • 感谢您提供的示例。我从上面的回复中使用了 webClient.DownloadFileTaskAsync 并且有效,但从长远来看,我可能想做多线程,这样我就可以得到一个进度条,并希望同时下载多个东西(或同时下载)时间)谢谢!
      • @ErnieCooper,对于搜索者来说,将 Eser 的答案标记为答案可能会更有用,这是值得称赞的一点:)。
      猜你喜欢
      • 2012-04-14
      • 1970-01-01
      • 2013-04-17
      • 1970-01-01
      • 2011-07-12
      • 2018-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多