在 Visual Studio 2017 年使用 .net 4.5+ 测试异步方法似乎会阻塞两个线程。 运行 .net core 2.0 中的相同代码不阻塞
Testing async methods in Visual Studio 2017 with .net 4.5+ appear to block on two threads. Running the same code in .net core 2.0 does not block
最近几天我遇到了一个比较痛苦的问题。
在 Visual Studio 2017 年执行旨在验证异步行为的单元测试时,异步方法调用似乎被阻止,但只有当我调用网络时 API(我保证,我首先在本地模拟服务,但随后需要使用 API 进行一些集成测试以证明 Web 请求的行为类似)。
我终于打电话给朋友帮我想通了,他重新编写了相同的测试并显示他的测试表现符合预期。唯一不同的因素是他在 VS2017 中使用 .net Core 2.0 编写了测试。
示例代码:
[TestMethod]
public void TestRestSharpExecuteTaskAsync()
{
var tasks = new List<Task>();
Task.WhenAll(Enumerable.Range(1, 10).Select(async s =>
{
// Using RestSharp, but also tested using raw WebRequests
var restClient = new RestClient("http://google.com");
var request = new RestRequest("/", RestSharp.Method.GET);
Debug.WriteLine(string.Format("{0} | DEBUG: BEGIN-ExecuteTaskAsync()", DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff")));
var stopwatch = new Stopwatch();
stopwatch.Start();
var response = await restClient.ExecuteTaskAsync(request);
stopwatch.Stop();
Debug.WriteLine(string.Format("{0} | DEBUG: END-ExecuteTaskAsync() completed in {1} milliseconds", DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff"), stopwatch.ElapsedMilliseconds));
})).Wait();
}
目前让我困惑的是,当这段代码在 .net 4.5+ 的 VS2017 中执行时(也测试了 4.6.2),结果显示明显有任务在等待:
11/30/2017 10:45:45.214 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.253 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.253 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.253 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.253 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.368 | DEBUG: END-ExecuteTaskAsync() completed in 151 milliseconds
11/30/2017 10:45:45.368 | DEBUG: END-ExecuteTaskAsync() completed in 115 milliseconds
11/30/2017 10:45:45.414 | DEBUG: END-ExecuteTaskAsync() completed in 161 milliseconds
11/30/2017 10:45:45.420 | DEBUG: END-ExecuteTaskAsync() completed in 166 milliseconds
11/30/2017 10:45:45.466 | DEBUG: END-ExecuteTaskAsync() completed in 212 milliseconds
11/30/2017 10:45:45.473 | DEBUG: END-ExecuteTaskAsync() completed in 218 milliseconds
11/30/2017 10:45:45.520 | DEBUG: END-ExecuteTaskAsync() completed in 266 milliseconds
11/30/2017 10:45:45.522 | DEBUG: END-ExecuteTaskAsync() completed in 268 milliseconds
11/30/2017 10:45:45.570 | DEBUG: END-ExecuteTaskAsync() completed in 315 milliseconds
11/30/2017 10:45:45.578 | DEBUG: END-ExecuteTaskAsync() completed in 323 milliseconds
但是,当 运行 在同一个 VS2017 IDE 中使用 .net Core 2.0 项目时,任务按预期异步执行:
11/30/2017 10:45:11.922 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.976 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.976 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 230 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 176 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 175 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 175 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 175 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 176 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 176 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 175 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 176 milliseconds
11/30/2017 10:45:12.154 | DEBUG: END-ExecuteTaskAsync() completed in 177 milliseconds
这是IDE配置吗? .net 4.x 中的错误?给出了什么?
编辑:当我到达一个端点时,问题变得更加明显我控制响应时间:
在 VS2017 中,.net Core 2.0:
12/01/2017 12:03:29.725 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.770 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.772 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4232 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4279 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4234 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4235 milliseconds
在 VS2017 中,.net 4.5:
12/01/2017 12:03:49.972 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.010 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:54.132 | DEBUG: END-ExecuteTaskAsync() completed in 4158 milliseconds
12/01/2017 12:03:54.132 | DEBUG: END-ExecuteTaskAsync() completed in 4121 milliseconds
12/01/2017 12:03:58.189 | DEBUG: END-ExecuteTaskAsync() completed in 8178 milliseconds
12/01/2017 12:03:58.191 | DEBUG: END-ExecuteTaskAsync() completed in 8180 milliseconds
12/01/2017 12:04:02.236 | DEBUG: END-ExecuteTaskAsync() completed in 12225 milliseconds
12/01/2017 12:04:02.239 | DEBUG: END-ExecuteTaskAsync() completed in 12228 milliseconds
12/01/2017 12:04:06.300 | DEBUG: END-ExecuteTaskAsync() completed in 16288 milliseconds
12/01/2017 12:04:06.302 | DEBUG: END-ExecuteTaskAsync() completed in 16290 milliseconds
12/01/2017 12:04:10.362 | DEBUG: END-ExecuteTaskAsync() completed in 20350 milliseconds
12/01/2017 12:04:10.364 | DEBUG: END-ExecuteTaskAsync() completed in 20352 milliseconds
同样,IDE 是相同的,代码是相同的。应用的差异是 .net 版本。
拦截与async
机制无关。
根据您在 .NET 4.5 中的日志,一次处理两个请求。您在 .NET 4.5+ 中的代码达到 2-connection-per-host 限制,默认情况下在 System.Net
:
中有效
System.Net uses two connections per application per host by default
Having said that you can increase the number of connections per host in the following way(s):
a) Increase the maximum for ALL hosts change the ServicePointManager.DefaultConnectionLimit
b) Increase the maximum for a specific host retrieve the ServicePoint for a host by calling
ServicePointManager.FindServicePoint and then change the ServicePoint.ConnectionLimit
Note that
a) ALL these changes are only app domain wide and will not affect other processes connection limits.
b) you are contending for the network bandwith with all other applications or threads in the same application; and if you increase the connection limit too large, you may starve other(s).
c) you need to experiment to find a sweet spot; for what a connection limit should be.
另请参阅:How can I programmatically remove the 2 connection limit in WebClient
在 .NET Core 中,默认情况下不施加此限制,根据:
最近几天我遇到了一个比较痛苦的问题。
在 Visual Studio 2017 年执行旨在验证异步行为的单元测试时,异步方法调用似乎被阻止,但只有当我调用网络时 API(我保证,我首先在本地模拟服务,但随后需要使用 API 进行一些集成测试以证明 Web 请求的行为类似)。
我终于打电话给朋友帮我想通了,他重新编写了相同的测试并显示他的测试表现符合预期。唯一不同的因素是他在 VS2017 中使用 .net Core 2.0 编写了测试。
示例代码:
[TestMethod]
public void TestRestSharpExecuteTaskAsync()
{
var tasks = new List<Task>();
Task.WhenAll(Enumerable.Range(1, 10).Select(async s =>
{
// Using RestSharp, but also tested using raw WebRequests
var restClient = new RestClient("http://google.com");
var request = new RestRequest("/", RestSharp.Method.GET);
Debug.WriteLine(string.Format("{0} | DEBUG: BEGIN-ExecuteTaskAsync()", DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff")));
var stopwatch = new Stopwatch();
stopwatch.Start();
var response = await restClient.ExecuteTaskAsync(request);
stopwatch.Stop();
Debug.WriteLine(string.Format("{0} | DEBUG: END-ExecuteTaskAsync() completed in {1} milliseconds", DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff"), stopwatch.ElapsedMilliseconds));
})).Wait();
}
目前让我困惑的是,当这段代码在 .net 4.5+ 的 VS2017 中执行时(也测试了 4.6.2),结果显示明显有任务在等待:
11/30/2017 10:45:45.214 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.253 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.253 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.253 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.253 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.368 | DEBUG: END-ExecuteTaskAsync() completed in 151 milliseconds
11/30/2017 10:45:45.368 | DEBUG: END-ExecuteTaskAsync() completed in 115 milliseconds
11/30/2017 10:45:45.414 | DEBUG: END-ExecuteTaskAsync() completed in 161 milliseconds
11/30/2017 10:45:45.420 | DEBUG: END-ExecuteTaskAsync() completed in 166 milliseconds
11/30/2017 10:45:45.466 | DEBUG: END-ExecuteTaskAsync() completed in 212 milliseconds
11/30/2017 10:45:45.473 | DEBUG: END-ExecuteTaskAsync() completed in 218 milliseconds
11/30/2017 10:45:45.520 | DEBUG: END-ExecuteTaskAsync() completed in 266 milliseconds
11/30/2017 10:45:45.522 | DEBUG: END-ExecuteTaskAsync() completed in 268 milliseconds
11/30/2017 10:45:45.570 | DEBUG: END-ExecuteTaskAsync() completed in 315 milliseconds
11/30/2017 10:45:45.578 | DEBUG: END-ExecuteTaskAsync() completed in 323 milliseconds
但是,当 运行 在同一个 VS2017 IDE 中使用 .net Core 2.0 项目时,任务按预期异步执行:
11/30/2017 10:45:11.922 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.976 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.976 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 230 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 176 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 175 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 175 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 175 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 176 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 176 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 175 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 176 milliseconds
11/30/2017 10:45:12.154 | DEBUG: END-ExecuteTaskAsync() completed in 177 milliseconds
这是IDE配置吗? .net 4.x 中的错误?给出了什么?
编辑:当我到达一个端点时,问题变得更加明显我控制响应时间:
在 VS2017 中,.net Core 2.0:
12/01/2017 12:03:29.725 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.770 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.772 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4232 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4279 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4234 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4235 milliseconds
在 VS2017 中,.net 4.5:
12/01/2017 12:03:49.972 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.010 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:54.132 | DEBUG: END-ExecuteTaskAsync() completed in 4158 milliseconds
12/01/2017 12:03:54.132 | DEBUG: END-ExecuteTaskAsync() completed in 4121 milliseconds
12/01/2017 12:03:58.189 | DEBUG: END-ExecuteTaskAsync() completed in 8178 milliseconds
12/01/2017 12:03:58.191 | DEBUG: END-ExecuteTaskAsync() completed in 8180 milliseconds
12/01/2017 12:04:02.236 | DEBUG: END-ExecuteTaskAsync() completed in 12225 milliseconds
12/01/2017 12:04:02.239 | DEBUG: END-ExecuteTaskAsync() completed in 12228 milliseconds
12/01/2017 12:04:06.300 | DEBUG: END-ExecuteTaskAsync() completed in 16288 milliseconds
12/01/2017 12:04:06.302 | DEBUG: END-ExecuteTaskAsync() completed in 16290 milliseconds
12/01/2017 12:04:10.362 | DEBUG: END-ExecuteTaskAsync() completed in 20350 milliseconds
12/01/2017 12:04:10.364 | DEBUG: END-ExecuteTaskAsync() completed in 20352 milliseconds
同样,IDE 是相同的,代码是相同的。应用的差异是 .net 版本。
拦截与async
机制无关。
根据您在 .NET 4.5 中的日志,一次处理两个请求。您在 .NET 4.5+ 中的代码达到 2-connection-per-host 限制,默认情况下在 System.Net
:
System.Net uses two connections per application per host by default
Having said that you can increase the number of connections per host in the following way(s):
a) Increase the maximum for ALL hosts change the ServicePointManager.DefaultConnectionLimit
b) Increase the maximum for a specific host retrieve the ServicePoint for a host by calling ServicePointManager.FindServicePoint and then change the ServicePoint.ConnectionLimit
Note that
a) ALL these changes are only app domain wide and will not affect other processes connection limits.
b) you are contending for the network bandwith with all other applications or threads in the same application; and if you increase the connection limit too large, you may starve other(s). c) you need to experiment to find a sweet spot; for what a connection limit should be.
另请参阅:How can I programmatically remove the 2 connection limit in WebClient
在 .NET Core 中,默认情况下不施加此限制,根据: