请不要在ASP.NET MVC的异步操作使用一个线程线程池在.NET 4 [英] Do asynchronous operations in ASP.NET MVC use a thread from ThreadPool on .NET 4

查看:1796
本文介绍了请不要在ASP.NET MVC的异步操作使用一个线程线程池在.NET 4的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  

这个问题后,使用异步时,让我舒服   操作ASP.NET MVC中。所以,我写了两篇博客文章上:

     
      
  • <一个href="http://www.tugberkugurlu.com/archive/my-take-on-task-base-asynchronous-programming-in-c-sharp-5-0-and-asp-net-mvc-web-applications">My在C#5.0和ASP.NET MVC的Web应用程序以在基于任务的异步编程
  •   
  • <一个href="http://www.tugberkugurlu.com/archive/asynchronous-database-calls-with-task-based-asynchronous-programming-model-tap-in-asp-net-mvc-4">Asynchronous数据库调用随着基于任务的异步编程模型(TAP)的ASP.NET MVC 4
  •   

我有太多的误解,在我的脑海里关于ASP.NET MVC的异步操作。

我总是听到这样一句话: 应用程序可以扩展更好,如果操作异步运行

和我听到这样的句子很多,以及为如果您有交通量巨大,你可能会更好不执行你的查询异步 - 耗时2个额外的线程来服务一个请求花费资源从其他传入的请求。

我想,这两句话是不一致的。

我没有怎么线程池工作在ASP.NET多的信息,但我知道,线程池拥有线程大小有限。所以,第二句有可能与这个问题。

和我想知道,如果在ASP.NET MVC中的异步操作使用一个线程的线程池在.NET 4?

例如,当我们执行一个AsyncController,请问该应用程序的结构?如果我得到巨大的流量,它是实现AsyncController一个好主意?

有没有人在那里谁可以在我眼前带走这个黑幕,并解释我对异步处理的ASP.NET MVC 3(NET 4)?

编辑:

我已阅读本文件下方近数百次,我理解主要的交易,但我仍然有困惑,因为有太多的不一致意见在那里。

在ASP.NET MVC 使用异步控制器

编辑:

让我们假设我有控制器操作如下图所示(不是 AsyncController 虽然实现):

 公开的ViewResult指数(){

    Task.Factory.StartNew(()=&GT; {
        //这里做一个先进的looging这需要一段时间
    });

    返回查看();
}
 

当你看到这里,我火的操作,并忘掉它。于是,我立即返回,而不等待它完成。

在这种情况下,这是否有使用一个线程从线程池?如果是这样,它完成后,会发生什么情况该线程?请问 GC 进来,清理它结束后立即?

编辑:

对于@达林的答案,这里是异步code的样品进行对话的数据库:

 公共类FooController的:AsyncController {

    // EF 4.2的DbContext实例
    MyContext _context =新MyContext();

    公共无效IndexAsync(){

        AsyncManager.OutstandingOperations.Increment(3);

        任务&LT; IEnumerable的&LT;富&GT;&GT; .Factory.StartNew(()=&GT; {

           返回
                _context.Foos;
        })ContinueWith(T =&GT; {

            AsyncManager.Parameters [FOOS] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        任务&LT; IEnumerable的&LT;酒吧&GT;&GT; .Factory.StartNew(()=&GT; {

           返回
                _context.Bars;
        })ContinueWith(T =&GT; {

            AsyncManager.Parameters [条] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        任务&LT; IEnumerable的&LT; FooBar的&GT;&GT; .Factory.StartNew(()=&GT; {

           返回
                _context.FooBars;
        })ContinueWith(T =&GT; {

            AsyncManager.Parameters [foobars] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });
    }

    公众的ViewResult IndexCompleted(
        IEnumerable的&LT;富&GT; FOOS,
        IEnumerable的&LT;酒吧GT;酒吧,
        IEnumerable的&LT; FooBar的&GT; foob​​ars){

        //做定期的东西和回报

    }
}
 

解决方案

下面是一个优秀的文​​章我建议你阅读,以便更好地理解异步处理在ASP.NET(这是异步控制器基本上重新present)。

让我们首先考虑一个标准的同步动作:

 公众的ActionResult指数()
{
    //一些处理
    返回查看();
}
 

当一个请求发送到这个动作一线程从线程池中提取和这个动作的身体上该线程被执行。因此,如果此动作内部的处理是要阻止该线程为整个处理速度慢,所以该线程不能重复使用,以处理其他请求。在请求执行结束,线程返回到线程池。

现在,让我们的异步模式的一个例子:

 公共无效IndexAsync()
{
    //执行一些处理
}

公众的ActionResult IndexCompleted(对象结果)
{
    返回查看();
}
 

当一个请求被发送到索引操作,一个线程从线程池和 IndexAsync的体绘制的执行方法。一旦该方法的主体完成执行,该线程返回到线程池。然后,使用标准的 AsyncManager.OutstandingOperations ,一旦信号完成异步操作,另一个线程从线程池和的 IndexCompleted 的行动就可以了,结果呈现到客户端执行。

那么,我们就可以在这个模式中看到的是,一个单一的客户端的HTTP请求可以通过两个不同的线程来执行。

现在最有趣的部分发生在 IndexAsync 方法内。如果你在它里面阻塞操作,你是完全浪费了异步控制器,因为你挡了工作线程的整个目的(请记住,这个动作的主体从线程池中绘制的线程中执行)。

所以,我们什么时候可以利用异步控制器,你可能会问,真正的优势在哪里?

恕我直言,我们可以得到大多数的时候,我们有I / O密集​​型操作(如数据库和网络调用远程服务)。如果你有一个CPU密集型操作,异步操作不会给你带来多少好处。

那么,我们为什么能够获得受益于I / O密集​​型操作?因为我们可以使用 I / O完成端口。 IOCP是非常强大的,因为你的整个操作的执行过程中不消耗任何服务器上的线程或资源。

它们是如何工作的?

假设我们要使用到下载远程网页中的内容的<一href="http://msdn.microsoft.com/en-us/library/system.net.webclient.downloadstringasync.aspx">WebClient.DownloadStringAsync方法。调用此方法将在操作系统中注册一个IOCP并立即返回。在整个请求的处理,没有任何线程占用您的服务器上。一切都发生在远程服务器上。这可能需要大量的时间,但你不在乎,你不破坏你的工作线程。一旦收到响应IOCP的发出信号,一线程从线程池中提取和回调在此线程执行。但正如你所看到的,在整个过程中,我们没有垄断任何线程。

同样代表真正与方法,如FileStream.BeginRead,SqlCommand.BeginExecute,...

什么并行多个数据库调用?假设你有在你执行顺序4阻断数据库调用的同步控制器的动作。这很容易计算出,如果每个数据库调用需要200毫秒,你的控制器动作将花费大约800毫秒来执行。

如果您不需要按顺序运行这些调用,将并行他们提高性能?

这是一个大问题,这是不容易回答。也许是,也许不是。这将完全取决于你如何实现这些数据库调用。如果使用异步控制器和I / O完成端口的讨论previously你将拉动其他行动这个控制器操作和性能,以及,你将不会被垄断工作线程。

在另一方面,如果你不善实现它们(从线程池中的线程进行封闭数据库调用),则执行该操作的总时间基本上降低到大约200毫秒,但你会消耗掉4工人线程,这样就可能会降低其可能成为饥饿,因为在缺少池中的线程来处理他们的其他请求的性能。

所以这是非常困难的,如果你不觉得准备好您的应用进行了广泛的测试,没有实现异步控制器,因为有机会,你会比好处更大的伤害。实施这些只有当你有一个这样做的原因:例如,你已经确定了标准的同步控制器的动作是一个瓶颈,您的应用程序(执行广泛的负载测试,当然,测量后)

现在让我们考虑您的例子:

 公开的ViewResult指数(){

    Task.Factory.StartNew(()=&GT; {
        //这里做一个先进的looging这需要一段时间
    });

    返回查看();
}
 

当接收到一个请求的线程从线程池中提取执行其身体指数作用,但其机身仅调度使用TPL一个新的任务。这样的动作执行结束,线程返回到线程池。除,TPL使用从线程池中的线程来执行它们的处理。因此,即使原来的线程返回到线程池,你画的这个池中另一个线程来执行任务的主体。所以,你已经危及2线从precious游泳池。

现在让我们考虑以下内容:

 公开的ViewResult指数(){

    新的Thread(()=&GT; {
        //这里做一个先进的looging这需要一段时间
    })。开始();

    返回查看();
}
 

在这种情况下,我们手动生成一个线程。在这种情况下,指数动作的主体的执行可能需要稍长(因为产卵一个新的线程比从现有池绘图1更贵)。但先进的记录操作的执行将线程上这不是池的一部分来完成。因此,我们不能破坏这仍然免费提供另一个请求从池中的线程。

After this question, it makes me comfortable when using async operations in ASP.NET MVC. So, I wrote two blog posts on that:

I have too many misunderstandings in my mind about asynchronous operations on ASP.NET MVC.

I always hear this sentence: Application can scale better if operations run asynchronously

And I heard this kind of sentences a lot as well: if you have a huge volume of traffic, you may be better off not performing your queries asynchronously - consuming 2 extra threads to service one request takes resources away from other incoming requests.

I think those two sentences are inconsistent.

I do not have much information about how threadpool works on ASP.NET but I know that threadpool has a limited size for threads. So, the second sentence has to be related to this issue.

And I would like to know if asynchronous operations in ASP.NET MVC uses a thread from ThreadPool on .NET 4?

For example, when we implement a AsyncController, how does the app structures? If I get huge traffic, is it a good idea to implement AsyncController?

Is there anybody out there who can take this black curtain away in front of my eyes and explain me the deal about asynchrony on ASP.NET MVC 3 (NET 4)?

Edit:

I have read this below document nearly hundreds of times and I understand the main deal but still I have confusion because there are too much inconsistent comment out there.

Using an Asynchronous Controller in ASP.NET MVC

Edit:

Let's assume I have controller action like below (not an implementation of AsyncController though):

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

As you see here, I fire an operation and forget about it. Then, I return immediately without waiting it be completed.

In this case, does this have to use a thread from threadpool? If so, after it completes, what happens to that thread? Does GC comes in and clean up just after it completes?

Edit:

For the @Darin's answer, here is a sample of async code which talks to database:

public class FooController : AsyncController {

    //EF 4.2 DbContext instance
    MyContext _context = new MyContext();

    public void IndexAsync() { 

        AsyncManager.OutstandingOperations.Increment(3);

        Task<IEnumerable<Foo>>.Factory.StartNew(() => { 

           return 
                _context.Foos;
        }).ContinueWith(t => {

            AsyncManager.Parameters["foos"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        Task<IEnumerable<Bars>>.Factory.StartNew(() => { 

           return 
                _context.Bars;
        }).ContinueWith(t => {

            AsyncManager.Parameters["bars"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        Task<IEnumerable<FooBar>>.Factory.StartNew(() => { 

           return 
                _context.FooBars;
        }).ContinueWith(t => {

            AsyncManager.Parameters["foobars"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });
    }

    public ViewResult IndexCompleted(
        IEnumerable<Foo> foos, 
        IEnumerable<Bar> bars,
        IEnumerable<FooBar> foobars) {

        //Do the regular stuff and return

    }
}

解决方案

Here's an excellent article I would recommend you reading to better understand asynchronous processing in ASP.NET (which is what asynchronous controllers basically represent).

Let's first consider a standard synchronous action:

public ActionResult Index()
{
    // some processing
    return View();
}

When a request is made to this action a thread is drawn from the thread pool and the body of this action is executed on this thread. So if the processing inside this action is slow you are blocking this thread for the entire processing, so this thread cannot be reused to process other requests. At the end of the request execution, the thread is returned to the thread pool.

Now let's take an example of the asynchronous pattern:

public void IndexAsync()
{
    // perform some processing
}

public ActionResult IndexCompleted(object result)
{
    return View();
}

When a request is sent to the Index action, a thread is drawn from the thread pool and the body of the IndexAsync method is executed. Once the body of this method finishes executing, the thread is returned to the thread pool. Then, using the standard AsyncManager.OutstandingOperations, once you signal the completion of the async operation, another thread is drawn from the thread pool and the body of the IndexCompleted action is executed on it and the result rendered to the client.

So what we can see in this pattern is that a single client HTTP request could be executed by two different threads.

Now the interesting part happens inside the IndexAsync method. If you have a blocking operation inside it, you are totally wasting the whole purpose of the asynchronous controllers because you are blocking the worker thread (remember that the body of this action is executed on a thread drawn from the thread pool).

So when can we take real advantage of asynchronous controllers you might ask?

IMHO we can gain most when we have I/O intensive operations (such as database and network calls to remote services). If you have a CPU intensive operation, asynchronous actions won't bring you much benefit.

So why can we gain benefit from I/O intensive operations? Because we could use I/O Completion Ports. IOCP are extremely powerful because you do not consume any threads or resources on the server during the execution of the entire operation.

How do they work?

Suppose that we want to download the contents of a remote web page using the WebClient.DownloadStringAsync method. You call this method which will register an IOCP within the operating system and return immediately. During the processing of the entire request, no threads are consumed on your server. Everything happens on the remote server. This could take lots of time but you don't care as you are not jeopardizing your worker threads. Once a response is received the IOCP is signaled, a thread is drawn from the thread pool and the callback is executed on this thread. But as you can see, during the entire process, we have not monopolized any threads.

The same stands true with methods such as FileStream.BeginRead, SqlCommand.BeginExecute, ...

What about parallelizing multiple database calls? Suppose that you had a synchronous controller action in which you performed 4 blocking database calls in sequence. It's easy to calculate that if each database call takes 200ms, your controller action will take roughly 800ms to execute.

If you don't need to run those calls sequentially, would parallelizing them improve performance?

That's the big question, which is not easy to answer. Maybe yes, maybe no. It will entirely depend on how you implement those database calls. If you use async controllers and I/O Completion Ports as discussed previously you will boost the performance of this controller action and of other actions as well, as you won't be monopolizing worker threads.

On the other hand if you implement them poorly (with a blocking database call performed on a thread from the thread pool), you will basically lower the total time of execution of this action to roughly 200ms but you would have consumed 4 worker threads so you might have degraded the performance of other requests which might become starving because of missing threads in the pool to process them.

So it is very difficult and if you don't feel ready to perform extensive tests on your application, do not implement asynchronous controllers, as chances are that you will do more damage than benefit. Implement them only if you have a reason to do so: for example you have identified that standard synchronous controller actions are a bottleneck to your application (after performing extensive load tests and measurements of course).

Now let's consider your example:

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

When a request is received for the Index action a thread is drawn from the thread pool to execute its body, but its body only schedules a new task using TPL. So the action execution ends and the thread is returned to the thread pool. Except that, TPL uses threads from the thread pool to perform their processing. So even if the original thread was returned to the thread pool, you have drawn another thread from this pool to execute the body of the task. So you have jeopardized 2 threads from your precious pool.

Now let's consider the following:

public ViewResult Index() { 

    new Thread(() => { 
        //Do an advanced looging here which takes a while
    }).Start();

    return View();
}

In this case we are manually spawning a thread. In this case the execution of the body of the Index action might take slightly longer (because spawning a new thread is more expensive than drawing one from an existing pool). But the execution of the advanced logging operation will be done on a thread which is not part of the pool. So we are not jeopardizing threads from the pool which remain free for serving another requests.

这篇关于请不要在ASP.NET MVC的异步操作使用一个线程线程池在.NET 4的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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