使用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);
}
答案 0 :(得分:1)
<强> TL;博士强>
要修复您的具体示例,您需要致电Wait()
以同步等待Task
完成。 不 RunSynchronously()
。
但您可能希望await
Task
允许异步完成。在大多数情况下,Wait()
对性能不是很好,并且如果使用不明智,可能会导致死锁。
长解释
RunSynchronously()
与Wait()
的用法略有不同。它同步运行 Task
,而Wait
将等待同步,但不会决定它应该如何运行。
RunSynchronously()
它在现代TPL使用中不是很有用。它只能在 cold Task
上调用 - 一个尚未启动的。
这不是很有用的原因是几乎每个返回Task
的库方法都会返回一个 hot Task
- 一个已经启动的方法。这包括来自HttpClient
的内容。当您在已经启动的Task
上调用它时,您会遇到刚刚遇到的异常。