在等待带有进度报告的DownloadFileAsync()时暂停主线程

时间:2017-05-18 18:17:08

标签: c# multithreading http wait

正如您在代码中间看到的那样,有一个丑陋的线程阻塞代码等待文件下载,并在命令行中启用进度报告。我的意思是,它仍然比Thread.Sleep()还要忙或等待,对吧?无论如何,我确实知道Wait/Pulse,但我不知道如何在这里申请。

我的代码是否完全重构以更好地适应单一异步操作最干净的解决方案?可以覆盖WebClient类中的某些内容来使用Wait/Pulse类型的等待吗?

项目和相关功能:Github

相关摘要:

static void GetPatch(KeyValuePair<string, string> entry, string part)
{
    string url = entry.Key,
        fname = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1),
        path = G.patchPath + "\\" + fname;
        bool exists = File.Exists(path);
    Console.Write(fname + " ... ");
    string message = "local";
    if ((exists && GetSHA1(path) != entry.Value) || !exists)
    {
        if (exists) File.Delete(path);
        G.wc.DownloadProgressChanged += Wc_DownloadProgressChanged;
        G.wc.DownloadFileAsync(new Uri(url), part);
        while (G.wc.IsBusy)
        {
            // There must be a better way
            new System.Threading.ManualResetEvent(false).WaitOne(200);
        }
        G.wc.DownloadProgressChanged -= Wc_DownloadProgressChanged;
        message = "done";
    }
    if (File.Exists(part)) File.Move(part, path);
    G.patchFNames.Enqueue(fname);
    Green(message);
}

private static void Wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    int p = e.ProgressPercentage;
    p = p < 100 ? p : 99;
    Console.Write("{0:00}%\b\b\b", p);
}

请耐心等待,这是我用C#编写的第一个项目,我是OOP和C#的绝对新手。

3 个答案:

答案 0 :(得分:2)

让我在评论中复制网址中的代码:

public void DownloadFile(Uri uri, string desintaion)
{
  using(var wc = new WebClient())
  {
    wc.DownloadProgressChanged += HandleDownloadProgress;
    wc.DownloadFileCOmpleted += HandleDownloadComplete;

    var syncObj = new Object();
    lock(syncObject)
    {
       wc.DownloadFileAsync(sourceUri, destination, syncObject);
       //This would block the thread until download completes
       Monitor.Wait(syncObject);
    }
  }

  //Do more stuff after download was complete
}

public void HandleDownloadComplete(object sender, AsyncCompletedEventArgs args)
{
   lock(e.UserState)
   {  
      //releases blocked thread
      Monitor.Pulse(e.UserState);
   }
}


public void HandleDownloadProgress(object sender, DownloadProgressChangedEventArgs args)
{
  //Process progress updates here
}

答案 1 :(得分:0)

您的高级代码应如下所示

public async Task StartFileDownload()
{
    var downloadTask = StartDownload();
    var monitoringTask = StartMonitoringProgress();

    await Task.WhenAll(downloadTask, monitoringTask);
}

您的监控任务应该是每N ms检查下载进度并更新进度条。虽然您不能直接执行此操作,因为您不在UI过程中,但您必须“分派”UI更新。

答案 2 :(得分:-1)

关于上述解决方案的注意事项:

如果异步下载未开始(例如,如果您在 WPF 应用程序内部),您可能需要设置 Thread SynchronizationContext 以使用线程池。如果这样做,则需要确保不会更改回调中的任何 UI 元素。

SynchronizationContext orig = SynchronizationContext.Current;
// Use thread pool and not the SyncContext for WPF
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
wc.DownloadDataAsync(new Uri(url), syncObject);
SynchronizationContext.SetSynchronizationContext(orig);