Вопрос

I am trying to write a multi threading search and then display all the results once the tasks have finished running but currently I don't understand how to process the results once all the tasks are complete

My code is as follows:

    private async void DoSearchAsync()
    {
        var productResults = await SearchProductsAsync(CoreCache.AllProducts);
        var brochureResults = await SearchBrochuresAsync(CoreCache.AllBrochures);

        _searchResults.AddRange(productResults); 
        _searchResults.AddRange(brochureResults);

        ResultsCount = _searchResults.Count;
    }

Where _searchResults is a List<SearchResult>

My understanding is that it will do both of the awaits simultaneously and then add the products to the search results. However when I call this in my controller:

    public ActionResult Index(string searchText)
    {
        SearchHelper helper = new SearchHelper(searchText);
        helper.DoSearchAsync();

        return View(helper);
    }

It is displaying the page before the searches are complete so no results are showing up. How do I make it wait for the results to finish before showing the page?

I've had a look into Tasks.Wait but don't know how to apply it to the above as it expects an array of tasks

    private Task<List<SearchResult>> SearchProductsAsync(IEnumerable<Product> products)
    {
        return Task<List<SearchResult>>.Factory.StartNew(() => GetProducts(products));
    }

    private Task<List<SearchResult>> SearchBrochuresAsync(IEnumerable<Assets> brochures)
    {
        return Task<List<SearchResult>>.Factory.StartNew(() => GetBrochures(brochures));
    }
Это было полезно?

Решение

Every time you call Factory.StartNew or Task.Run inside an ASP.NET controller, you grab a thread from ThreadPool. That thread could be otherwise serving another incoming HTTP request. So, your're really hurting your web app scalability. This may be a serious issue, depending on how many concurrent HTTP requests your web app is expected to receive.

Are you OK with that? If so, the code could look like this:

private async Task DoSearchAsync()
{
    var productResults = SearchProductsAsync(CoreCache.AllProducts);
    var brochureResults = SearchBrochuresAsync(CoreCache.AllBrochures);

    await Task.WhenAll(productResults, brochureResults);

    _searchResults.AddRange(productResults.Result); 
    _searchResults.AddRange(brochureResultsbrochure.Results);

    ResultsCount = _searchResults.Count;
}

public async Task<ActionResult> Index(string searchText)
{
    SearchHelper helper = new SearchHelper(searchText);

    await helper.DoSearchAsync();

    return View(helper);
}

Note I changed async void to async Task for DoSearchAsync, and made your controller method async, so it returns Task<ActionResult>.

Другие советы

My understanding is that it will do both of the awaits simultaneously

This is not correct Pete. The await means that it will pause execution of the current method until such time that the Async method being called completes. So your 2 searches will not run in Parallel.

For clarification, see the first line of the MSDN documentation....

The await operator is applied to a task in an asynchronous method to suspend the execution of the method until the awaited task completes.

You should use Task.WhenAll to wait for both searches to complete. As WhenAll supports returning Task<T> and both of your Async searches return a List<SearchResult> you can combine the results of both searches and await them both in one statement...

    var searchProducts = Task.Factory.StartNew(() => GetProducts(CoreCache.AllProducts));
    var searchBrochure = Task.Factory.StartNew(() => GetBrochures(CoreCache.AllBrochures));
    var allResults = await Task.WhenAll(new [] { searchProducts, searchBrochure });
    //allResults is List<SearchResult>[]

For completeness, its worth noting that allResults will contain 2 result sets. It does not perform a union on the 2 sets.

A complete working example for LinqPad can be found here on GitHub

You can use a task factory, then call wait on the resulting task:

Task taskA = Task.Factory.StartNew(() => DoSomeWork(10000000));
        taskA.Wait();
        Console.WriteLine("taskA has completed.");

http://msdn.microsoft.com/en-us/library/dd537610(v=vs.110).aspx

You can make it wait for the results to finish before displaying the page by making your function NOT be async.

Or if you want async you could move the search functions to be called by an AJAX call triggered when your page is loaded

You need to wait for the function to complete. In order to do that, first make it return a task:

private async Task DoSearchAsync()

then await it:

public async Task<ActionResult> Index(string searchText)
{
    SearchHelper helper = new SearchHelper(searchText);
    await helper.DoSearchAsync();

    return View(helper);
}

For more information, see http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top