ASP.NET C#5异步Web应用程序中使用异步和放大器;等待 [英] ASP.NET C#5 Asynchronous Web Applications Using Async & Await

查看:133
本文介绍了ASP.NET C#5异步Web应用程序中使用异步和放大器;等待的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

具有这个源研究异步Web发展的理念,特别是,我创建了一个示例应用程序来证明这个概念。

Having researched the concept of asynchronous web development, specifically from this source, I created a sample application to prove the concept.

该解决方案由2的ASP.NET Web API的应用程序。第一个是模拟慢端点;它会等待1000毫秒返回一个列表中的自定义类叫学生面前:

The solution is composed of 2 ASP.NET Web API applications. The first is a simulated slow endpoint; it waits for 1000 ms before returning a list a custom class called Student:

 public IEnumerable<Student> Get()
    {
        Thread.Sleep(1000);
        return new List<Student> { new Student { Name = @"Paul" }, new Student { Name = @"Steve" }, new Student { Name = @"Dave" }, new Student { Name = @"Sue" } };
    }

下面是Student类:

Here is the Student class:

public class Student
{
    public string Name { get; set; }
}

这端点在IIS 7托管在localhost:4002

This endpoint is hosted in IIS 7 on localhost:4002.

的第二应用接触第一使用2-端点,一种同步方式,另异步

The second application contacts the first using 2 endpoints, one synchronous, the other asynchronous:

public IEnumerable<Student> Get() {
        var proxy = WebRequest.Create(@"http://localhost:4002/api/values");

        var response = proxy.GetResponse();
        var reader = new StreamReader(response.GetResponseStream());

        return JsonConvert.DeserializeObject<IEnumerable<Student>>(reader.ReadToEnd());
    }

    public async Task<IEnumerable<Student>> Get(int id) {
        var proxy = new HttpClient();
        var getStudents = proxy.GetStreamAsync(@"http://localhost:4002/api/values");

        var stream = await getStudents;
        var reader = new StreamReader(stream);

        return JsonConvert.DeserializeObject<IEnumerable<Student>>(reader.ReadToEnd());
    }

其托管在IIS 7中在localhost:4001

It's hosted in IIS 7 on localhost:4001.

两个端点都按预期工作,并约返回英寸1秒。根据上面的链接视频在13:25,异步方法应该释放它的线程,减少冲突。

Both endpoints work as expected, and return in approx. 1 second. Based on the video in the link above at 13:25, the asynchronous method should release it's Thread, minimizing contention.

我在使用Apache台运行的应用程序性能测试。下面是同步方法的响应时间在10个并发请求:

I'm running performance tests on the application using Apache Bench. Here are the response times for the synchronous method with 10 concurrent requests:

这是不亚于我期望;更多的并发连接增加争,并延长响应时间。然而,这里是异步响应时间:

This is much as I'd expect; more concurrent connections increase contention and extend the response times. However, here are the asynchronous response times:

正如你所看到的,似乎还有一些争论。我本来期望的平均响应时间更加均衡。如果我运行与50个并发请求的两个端点的测试,我仍然得到相似的结果。

As you can see, there still seems to be some contention. I would have expected the average response times to be more balanced. If I run the tests on both endpoints with 50 concurrent requests, I still get similar results.

,似乎异步和同步方法在或多或少相同的速度(预期)运行时,不考虑在异步方法的开销,也表明异步方法似乎不被线程释放回线程池。我欢迎任何意见或澄清,谢谢。

Based on this, it seems that both asynchronous and synchronous methods are running at more or less the same speed (expected), not taking into account the overhead in asynchronous methods, but also that the asynchronous method doesn't seem to be releasing Threads back to the ThreadPool. I'd welcome any comments or clarifications, thanks.

推荐答案

我觉得有你不测试的内容你认为你测试pretty很好的机会。据我所知,你试图通过比较时序和​​推导线程注入检测释放回线程池中。

I think there's a pretty good chance you're not testing what you think you're testing. From what I can gather, you're trying to detect releases back to the thread pool by comparing timings and deducing thread injection.

首先,线程池的默认设置的.NET 4.5是非常高的。你不会只有10个或100个并发请求打他们。

For one thing, the default settings for the thread pool on .NET 4.5 are extremely high. You're not going to hit them with just 10 or 100 simultaneous requests.

退一步一秒钟,想想你要考什么?确实异步方法它的线程返回线程池

Step back for a second and think of what you want to test: does an async method return its thread to the thread pool?

我有一个演示,我展示给证明这一点。我不想创建我的演示了沉重的负载测试(在我的presentation笔记本电脑上运行),所以我把一个小窍门:我人为地限制线程池一个比较合理的值

I have a demo that I show to demonstrate this. I didn't want to create a heavy load test for my demo (running on my presentation laptop), so I pulled a little trick: I artificially restrict the thread pool to a more reasonable value.

一旦你这样做,你的测试很简单:执行多个连接,然后执行很多的加上一个的。同步执行将不得不等待一到开始最后一个之前完成,而异步实现将能够启动它们所有

Once you do that, your test is quite simple: perform that many simultaneous connections, and then perform that many plus one. The synchronous implementation will have to wait for one to complete before starting the last one, while the asynchronous implementation will be able to start them all.

在服务器端,第一限制线程池线程系统中的处理器的数目:

On the server side, first restrict the thread pool threads to the number of processors in the system:

protected void Application_Start()
{
    int workerThreads, ioThreads;
    ThreadPool.GetMaxThreads(out workerThreads, out ioThreads);
    ThreadPool.SetMaxThreads(Environment.ProcessorCount, ioThreads);
    ...
}

然后做同步和异步实现:

Then do the synchronous and asynchronous implementations:

public class ValuesController : ApiController
{
    // Synchronous
    public IEnumerable<string> Get()
    {
        Thread.Sleep(1000);
        return new string[] { "value1", "value2" };
    }

    // Asynchronous
    public async Task<IEnumerable<string>> Get(int id)
    {
        await Task.Delay(1000);
        return new string[] { "value1", "value2" };
    }
}

和最终客户的测试code:

And finally the client testing code:

static void Main(string[] args)
{
    try
    {
        MainAsync().Wait();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }

    Console.ReadKey();
}

static async Task MainAsync()
{
    ServicePointManager.DefaultConnectionLimit = int.MaxValue;

    var sw = new Stopwatch();
    var client = new HttpClient();
    var connections = Environment.ProcessorCount;
    var url = "http://localhost:35697/api/values/";

    await client.GetStringAsync(url); // warmup
    sw.Start();
    await Task.WhenAll(Enumerable.Range(0, connections).Select(i => client.GetStringAsync(url)));
    sw.Stop();
    Console.WriteLine("Synchronous time for " + connections + " connections: " + sw.Elapsed);

    connections = Environment.ProcessorCount + 1;

    await client.GetStringAsync(url); // warmup
    sw.Restart();
    await Task.WhenAll(Enumerable.Range(0, connections).Select(i => client.GetStringAsync(url)));
    sw.Stop();
    Console.WriteLine("Synchronous time for " + connections + " connections: " + sw.Elapsed);

    url += "13";
    connections = Environment.ProcessorCount;

    await client.GetStringAsync(url); // warmup
    sw.Restart();
    await Task.WhenAll(Enumerable.Range(0, connections).Select(i => client.GetStringAsync(url)));
    sw.Stop();
    Console.WriteLine("Asynchronous time for " + connections + " connections: " + sw.Elapsed);

    connections = Environment.ProcessorCount + 1;

    await client.GetStringAsync(url); // warmup
    sw.Restart();
    await Task.WhenAll(Enumerable.Range(0, connections).Select(i => client.GetStringAsync(url)));
    sw.Stop();
    Console.WriteLine("Asynchronous time for " + connections + " connections: " + sw.Elapsed);
}

在我的(8-逻辑核心)的机器,我看到的输出是这样的:

On my (8-logical-core) machine, I see output like this:

Synchronous time for 8 connections: 00:00:01.0194025
Synchronous time for 9 connections: 00:00:02.0362007
Asynchronous time for 8 connections: 00:00:01.0413737
Asynchronous time for 9 connections: 00:00:01.0238674

这清楚地表明,异步方法返回其线程的线程池。

Which clearly shows that the asynchronous method is returning its thread to the thread pool.

这篇关于ASP.NET C#5异步Web应用程序中使用异步和放大器;等待的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆