我在 C# 中有一个 Windows Phone 7 (7.1) 方法,它给定一个字符串形式的 URL,将该 URL 的内容下载到一个文件中(参见下面的代码)。从代码中可以看出,我分配了一个DownloadProgressChanged()事件处理程序。在该处理程序中,如果调用者提供了IProgress对象,我将调用该对象的Report()方法。考虑到用户的网络连接速度可能很慢,我想确保下载速度尽可能快。在WebClient 的DownloadProgressChanged()回调中调用IProgress.Report()方法会大大减慢下载速度吗?
我对IProgress.Report()不够熟悉,无法知道它是在当前线程还是调用线程上执行。它是否在调用线程上执行?我担心的是重复的线程切换真的会让事情陷入困境。我可能会将对该方法的调用包装在Task.Run()调用中,以保持 UI 线程满意。但以防万一,我会问我的代码是否有任何潜在的问题,就陷入 UI 线程而言?
对与结构或性能有关的代码的任何其他评论表示赞赏。请注意,我在此应用中使用Microsoft.Bcl.Async包。
更新(稍后添加):关于线程切换,显然在 UI 线程上引发了 DownloadProgressChanged() 事件,而不是在下载线程上,因此无需对 Dispatcher 等进行任何花哨的操作来进行 UI 更新设想。至少根据此代码项目文章:
public static void URLToFile(string strUrl, string strDestFilename, IProgress<int> progress, int iNumSecondsToWait = 30)
{
strUrl = strUrl.Trim();
if (String.IsNullOrWhiteSpace(strUrl))
throw new ArgumentException("(Misc::URLToFile) The URL is empty.");
strDestFilename = strDestFilename.Trim();
if (String.IsNullOrWhiteSpace(strDestFilename))
throw new ArgumentException("(Misc::URLToFile) The destination file name is empty.");
if (iNumSecondsToWait < 1)
throw new ArgumentException("(Misc::URLToFile) The number of seconds to wait is less than 1.");
// Create the isolated storage file.
StreamWriter sw = openIsoStorFileAsStreamWriter(strDestFilename);
// If the stream writer is NULL, then the file could not be created.
if (sw == null)
throw new System.IO.IOException("(Misc::URLToFile) Error creating or writing to the file named: " + strDestFilename);
// Asynchronous download. Note, the Silverlight version of WebClient does *not* implement
// IDisposable.
WebClient wc = new WebClient();
try
{
// Create a download progress changed handler so we can pass on progress
// reports to the caller if they provided a progress report object.
wc.DownloadProgressChanged += (s, e) =>
{
// Do we have a progress report handler?
if (progress != null)
// Yes, call it.
progress.Report(e.ProgressPercentage);
};
// Use a Lambda expression for the "completed" handler
// that writes the contents to a file.
wc.OpenReadCompleted += (s, e) =>
e.Result.CopyTo(sw.BaseStream);
// Now make the call to download the file.
wc.DownloadStringAsync(new Uri(strUrl));
}
finally
{
// Make sure the stream is cleaned up.
sw.Flush();
sw.Close();
// Make sure the StreamWriter is diposed of.
sw.Dispose();
} // try/finally
// CancellationTokenSource srcCancelToken = new CancellationTokenSource();
// srcCancelToken.CancelAfter(TimeSpan.FromSeconds(iNumSecondsToWait));
} // public static void URLToFile()