2017-05-18 33 views
1

正如您在代码的中间看到的,有一个丑陋的线程阻塞代码,它等待文件下载并在命令行中启用进度报告。我的意思是,它还是比Thread.Sleep()还好,或者正在等待,对不对?无论如何,我知道Wait/Pulse,但我不知道如何在这里应用。在等待带有进度报告的DownloadFileAsync()的同时暂停主线程

是我的代码的一个完整的重构,以更好地适应单一的异步操作最干净的解决方案?是否可以在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#的新手。

+0

你绝对需要在代码和清晰度上工作,但总的来说,我认为你是在一个正确的方式。您需要2个异步进程,一个用于文件下载,另一个用于监视进度。第二个进程必须是异步的,这样你才不会阻塞UI,你将不得不使用调度器来更新UI。 – Andrei

+0

你在找什么? https://alexfeinberg.wordpress.com/2014/09/14/how-to-use-net-webclient-synchronously-and-still-receive-progress-updates/ – MistyK

+0

如何转向async \ await?有'DownloadFileTaskAsync'可以等待,这将在这个代码中丢弃大部分的丑陋。 – Evk

回答

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 
} 
+0

是的,完美的。我知道userToken过载是关键,但令人遗憾的是MSDN不是非常具有描述性/缺少示例。 – user1263513

0

你的高级代码看起来应该是这样

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

    await Task.WhenAll(downloadTask, monitoringTask); 
} 

你的监视任务应该检查下载进度每N毫秒并更新进度栏。虽然你不能直接做到这一点,因为你不在UI过程中,你将不得不“发送”UI更新。

+1

乍一看,你的代码建议他使用'DownloadFileTaskAsync()',在实际中他使用'使用[基于事件的异步模式(EAP)](https:// msdn)的'DownloadFileAsync()'返回'Task'。 microsoft.com/en-us/library/wewwczdw(v=vs.110).aspx)。 – Cameron