在ASP.NET MVC 3 Action方法中并行运行任务

时间:2011-04-06 23:29:25

标签: asp.net-mvc linq asp.net-mvc-3 c#-4.0 parallel-processing

我在ASP.NET MVC 3应用中有一个搜索操作,它返回包含的建议以及带有给定关键字的标记

[HttpPost]
public ActionResult Search(string query, int pg = 0)
{
    var keywords = query.Split(new[] { ' ', ',', ';' }, 
        StringSplitOptions.RemoveEmptyEntries);

    var containing = (from s in _readonlySession.All<Suggestion>()
                      from k in keywords
                      where (s.Text.ToLower().Contains(k.ToLower()))
                      orderby s.Text
                      select s).Distinct();

    var tagged = (from t in _readonlySession.All<Tag>()
                  from s in t.Suggestions
                  from k in keywords
                  where t.Text.ToLower().Contains(k.ToLower())
                  orderby s.Text
                  select s).Distinct();

    var model = new SearchViewModel
    {
        Query = query,
        Containing = containing.ToList(),
        Tagged = tagged.ToList()
    };

    return View(model);
}

我认为containingtagged查询可以完美并行运行。

同时触发这两个查询的最佳方法是什么,等待结果,并且仅在两个查询完成时返回?

3 个答案:

答案 0 :(得分:8)

任务Parallels库是您的最佳选择。通过Google获取大量信息,但下面是您的实施可能的样子。

[HttpPost]
public ActionResult Search(string query, int pg = 0)
{
    var keywords = query.Split(new[] { ' ', ',', ';' }, 
        StringSplitOptions.RemoveEmptyEntries);

    IEnumerable containing=null;
    Task t1 = Task.Factory.StartNew(() =>
    {
        containing = (from s in _readonlySession.All<Suggestion>()
                        from k in keywords
                        where (s.Text.ToLower().Contains(k.ToLower()))
                        orderby s.Text
                        select s).Distinct().ToList();

    });

    IEnumerable tagged=null;
    Task t2 = Task.Factory.StartNew(() =>
    {
        var tagged = (from t in _readonlySession.All<Tag>()
                        from s in t.Suggestions
                        from k in keywords
                        where t.Text.ToLower().Contains(k.ToLower())
                        orderby s.Text
                        select s).Distinct().ToList();
    });

    t1.Wait();
    t2.Wait();

    var model = new SearchViewModel
    {
        Query = query,
        Containing = containing.ToList(),
        Tagged = tagged.ToList()
    };

    return View(model);
}

请记住,如果您的应用程序收到大量请求,那么异步执行查询可能会更好 - 消耗2个额外的线程来为一个请求提供服务会使资源远离其他传入请求。如果您拥有大量流量,或者您的硬件不足,这只会是一个问题。

答案 1 :(得分:3)

您希望在网页中使用TPL时要小心,因为TPL将利用本来用于处理其他HTTP请求的线程。从本质上讲,您可以在单个请求上降低响应延迟,降低请求的总体吞吐量。

有关权衡的更多解释,请参阅以下文章:

http://blogs.msdn.com/b/pfxteam/archive/2010/02/08/9960003.aspx

答案 2 :(得分:2)

这可能是async controller的良好候选人。关于异步控制器的重要一点是,如果您希望在这种情况下获得净性能增益,则需要修改存储库层并提供两种方法的异步版本,这两种方法在与远程系统(如远程系统)通信时理想地使用I / O完成端口数据库。

public class SearchController: AsyncController
{
    public void SearchAsync(string query, int pg = 0)
    {
        // TODO: Write a custom model binder to do this job
        // and have your controller action directly take an
        // IEnumerable<string> argument.
        var keywords = query.Split(new[] { ' ', ',', ';' }, 
            StringSplitOptions.RemoveEmptyEntries);

        AsyncManager.OutstandingOperations.Increment();
        AsyncManager.OutstandingOperations.Increment();

        _readonlySession.GetContainingCompleted += (sender, e) =>
        {
            AsyncManager.Parameters["containing"] = e.Value;
            AsyncManager.OutstandingOperations.Decrement();
        };

        _readonlySession.GetTaggedCompleted += (sender, e) =>
        {
            AsyncManager.Parameters["tagged"] = e.Value;
            AsyncManager.OutstandingOperations.Decrement();
        };

        _readonlySession.GetContainingAsync(keywords);
        _readonlySession.GetTaggedAsync(keywords);

        AsyncManager.Parameters["query"] = query;
    }

    public ActionResult SearchCompleted(string query, IEnumerable<Suggestion> containing, IEnumerable<Tag> tagged)
    {
        var model = new SearchViewModel
        {
            Query = query,
            Containing = containing.ToList(),
            Tagged = tagged.ToList()
        };
        return View(model);
    }
}