【问题标题】:C# Download Speed AsynchronouslyC#异步下载速度
【发布时间】:2015-08-13 14:32:57
【问题描述】:

我正在尝试获取当前用户的网络下载速度。在使用 NetworkInterfaces 遇到死胡同后,我尝试了在网上找到的解决方案。我对其进行了一些编辑,效果很好,但它不是异步的。

public static void GetDownloadSpeed(this Label lbl) 
{
    double[] speeds = new double[5];
    for (int i = 0; i < 5; i++)
    {
        int fileSize = 407; //Size of File in KB.
        WebClient client = new WebClient();
        DateTime startTime = DateTime.Now;
        if (!Directory.Exists($"{CurrentDir}/tmp/speedtest"))
            Directory.CreateDirectory($"{CurrentDir}/tmp/speedtest");

        client.DownloadFile(new Uri("https://ajax.googleapis.com/ajax/libs/threejs/r69/three.min.js"), "/tmp/speedtest/three.min.js");
        DateTime endTime = DateTime.Now;
        speeds[i] = Math.Round((fileSize / (endTime - startTime).TotalSeconds));
    }

    lbl.Text = string.Format("{0}KB/s", speeds.Average());
}

该函数在计时器内以 2 分钟的间隔被调用。

MyLbl.GetDownloadSpeed()

我尝试过使用 WebClient.DownloadFileAsync 但这只是显示无限符号。我的下一个尝试是使用 HttpClient 但在我继续之前,有没有人推荐一种异步获取当前用户下载速度的方法(不落后于主 GUI 线程)?

【问题讨论】:

  • 您应该包含您尝试使用DownloadFileAsync 的代码。我怀疑你在结果任务的某处调用ResultWait 导致了死锁。
  • 试试 `await Task.Run(()=> { //你的代码 });
  • 谢谢@PhilippeParé 成功了!
  • @DanielKelley 我将方法设为异步并在其上调用了 await。
  • @PhilippeParé 您能否将您的评论作为答案,以便我将其标记为答案?

标签: c# asynchronous


【解决方案1】:

正如建议的那样,您可以制作 GetDownloadSpeed() 的异步版本:

    async void GetDownloadSpeedAsync(this Label lbl, Uri address, int numberOfTests)
    {
        string directoryName = @"C:\Work\Test\speedTest";
        string fileName = "tmp.dat";

        if (!Directory.Exists(directoryName))
            Directory.CreateDirectory(directoryName);

        Stopwatch timer = new Stopwatch();

        timer.Start();

        for (int i = 0; i < numberOfTests; ++i)
        {
            using (WebClient client = new WebClient())
            {
                await client.DownloadFileTaskAsync(address, Path.Combine(directoryName, fileName), CancellationToken.None);
            }
        }

        lbl.Text == Convert.ToString(timer.Elapsed.TotalSeconds / numberOfTests);
    }

WebClient 类比较老,没有 awaitable DownloadFileAsync()

已编辑 正如正确指出的那样,WebClient 实际上有一个基于任务的异步方法 DownloadFileTaskAsync(),我建议使用它。下面的代码仍然可以帮助解决返回 Task 的异步方法未提供的情况。

我们可以在TaskCompletionSource&lt;T&gt; 的帮助下修复它:

    public static class WebClientExtensions
    {

        public static Task DownloadFileAwaitableAsync(this WebClient instance, Uri address, 
            string fileName, CancellationToken cancellationToken)
        {
            TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();

            // Subscribe for completion event
            instance.DownloadFileCompleted += instance_DownloadFileCompleted;

            // Setup cancellation
            var cancellationRegistration = cancellationToken.CanBeCanceled ? (IDisposable)cancellationToken.Register(() => { instance.CancelAsync(); }) : null;

            // Initiate asyncronous download 
            instance.DownloadFileAsync(address, fileName, Tuple.Create(tcs, cancellationRegistration));

            return tcs.Task;
        }

        static void instance_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            ((WebClient)sender).DownloadDataCompleted -= instance_DownloadFileCompleted;
            var data = (Tuple<TaskCompletionSource<object>, IDisposable>)e.UserState;
            if (data.Item2 != null) data.Item2.Dispose();
            var tcs = data.Item1;

            if (e.Cancelled)
            {
                tcs.TrySetCanceled();
            }
            else if (e.Error != null)
            {
                tcs.TrySetException(e.Error);
            }
            else
            {
                tcs.TrySetResult(null);
            }
        }
    }

【讨论】:

  • @PeterDuniho:感谢您的纠正,答案已编辑以包含您的 cmets。
【解决方案2】:

试试 `await Task.Run(()=> { //你的代码 });

编辑:@JustDevInc 我仍然认为您应该使用 DownloadAsync。 Task.Run(delegate) 创建一个新线程,您可能希望避免这种情况。如果您愿意,请发布您的一些旧代码,以便我们尝试修复它。

编辑:第一个解决方案原来是两个工作中唯一的一个。 DownloadFileAsync 不返回任务,所以不能等待。

【讨论】:

  • 尝试制作GetDownloadSpeedasync,使其返回Task,然后在DownloadFileAsync上返回await
  • 但是没有什么可以等待的吗? downloadFileAsync 返回一个 void
  • 你说得对,我刚刚检查了 MSDN 上的签名,这种方法应该是异步将资源保存到本地文件。它不返回任务,因此无法等待。我猜第一个解决方案是正确的!
猜你喜欢
  • 2013-09-23
  • 2014-12-03
  • 2016-08-18
  • 1970-01-01
  • 1970-01-01
  • 2011-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多