HttpClient

时间:2017-03-09 03:24:09

标签: c# visual-studio-2017 .net-standard-1.6

使用HttpClient将WebClient代码从.Net Framework 4.6.1转换为NetStandard 1.6,并且遇到一个奇怪的问题。这是我的代码块:

public double TestDownloadSpeed(Server server, int simultaniousDownloads = 2, int retryCount = 2)
    {
        var testData = GenerateDownloadUrls(server, retryCount);

        return TestSpeed(testData, async (client, url) =>
        {
            var data = await client.GetByteArrayAsync(url);

            return data.Length;
        }, simultaniousDownloads);
    }

 public double TestUploadSpeed(Server server, int simultaniousUploads = 2, int retryCount = 2)
    {
        var testData = GenerateUploadData(retryCount);

        return TestSpeed(testData, async (client, uploadData) =>
        {
            client.PostAsync(server.Url, new StringContent(uploadData.ToString())).RunSynchronously();

            return uploadData[0].Length;
        }, simultaniousUploads);
    }

private static double TestSpeed<T>(IEnumerable<T> testData, Func<HttpClient, T, Task<int>> doWork, int concurencyCount = 2)
    {
        var timer = new Stopwatch();
        var throttler = new SemaphoreSlim(concurencyCount);

        timer.Start();

        var downloadTasks = testData.Select(async data =>
        {
            await throttler.WaitAsync().ConfigureAwait(true);
            var client = new CoreSpeedWebClient();
            try
            {
                var size = await doWork(client, data).ConfigureAwait(true);
                return size;
            }
            finally
            {
                client.Dispose();
                throttler.Release();
            }
        }).ToArray();

        Task.Run(() => downloadTasks);

        timer.Stop();

        double totalSize = downloadTasks.Sum(task => task.Result);

        return (totalSize * 8 / 1024) / ((double)timer.ElapsedMilliseconds / 1000);
    }

因此,当调用 TestDownloadSpeed 函数时,一切都按预期工作,但是当我调用 TestUploadSpeed 方法时,我得到错误InvalidOperationException:可能无法在任务上调用RunSynchronously没有绑定到委托,例如从异步方法返回的任务。*在TestSpeed的这一部分。

double totalSize = downloadTasks.Sum(task => task.Result);

我真的在试图弄清楚 TestUploadSpeed 中的内容正在酝酿之中。任何人都有任何暗示指出我正确的方向?

如果它有帮助,这里的.Net 4.6.1代码没有问题,所以我的翻译中的某些内容可能已关闭了吗?再加上原始代码的运行速度提高了约5倍,所以不确定是什么......

    public double TestDownloadSpeed(Server server, int simultaniousDownloads = 2, int retryCount = 2)
    {
        var testData = GenerateDownloadUrls(server, retryCount);

        return TestSpeed(testData, async (client, url) =>
        {
            var data = await client.DownloadDataTaskAsync(url).ConfigureAwait(false);
            return data.Length;
        }, simultaniousDownloads);
    }

    public double TestUploadSpeed(Server server, int simultaniousUploads = 2, int retryCount = 2)
    {
        var testData = GenerateUploadData(retryCount);
        return TestSpeed(testData, async (client, uploadData) =>
        {
            await client.UploadValuesTaskAsync(server.Url, uploadData).ConfigureAwait(false);
            return uploadData[0].Length;
        }, simultaniousUploads);
    }

    private static double TestSpeed<T>(IEnumerable<T> testData, Func<WebClient, T, Task<int>> doWork, int concurencyCount = 2)
    {
        var timer = new Stopwatch();
        var throttler = new SemaphoreSlim(concurencyCount);

        timer.Start();
        var downloadTasks = testData.Select(async data =>
        {
            await throttler.WaitAsync().ConfigureAwait(false);
            var client = new SpeedTestWebClient();
            try
            {
                var size = await doWork(client, data).ConfigureAwait(false);
                return size;
            }
            finally
            {
                client.Dispose();
                throttler.Release();
            }
        }).ToArray();

        Task.WaitAll(downloadTasks);
        timer.Stop();

        double totalSize = downloadTasks.Sum(task => task.Result);
        return (totalSize * 8 / 1024) / ((double)timer.ElapsedMilliseconds / 1000);
    }

1 个答案:

答案 0 :(得分:1)

<强> TL;博士

要修复您的具体示例,您需要致电Wait()以同步等待Task完成。 RunSynchronously()

但您可能希望await Task允许异步完成。在大多数情况下,Wait()对性能不是很好,并且如果使用不明智,可能会导致死锁。

长解释

RunSynchronously()Wait()的用法略有不同。它同步运行 Task,而Wait等待同步,但不会决定它应该如何运行。

RunSynchronously()它在现代TPL使用中不是很有用。它只能在 cold Task上调用 - 一个尚未启动的。

这不是很有用的原因是几乎每个返回Task的库方法都会返回一个 hot Task - 一个已经启动的方法。这包括来自HttpClient的内容。当您在已经启动的Task上调用它时,您会遇到刚刚遇到的异常。