2014-01-16 3 views
0

Я ищу запрос данных с разных конечных точек, внутри метода контроллера. Я только хочу вернуть View(), когда все эти запросы будут выполнены. Можно ли это сделать, и как это можно сделать?Создать тему, которая запускает несколько других потоков

Прямо сейчас я делаю что-то близкое к этому

class GetDemData 
{ 
    int count = 0; 
    int requestsCompleted = 0; 

    List<string> addresses = new List<string>(); 

    public void AddDataToBeCollected(string address) 
    { 
     adresses.Add(address); 
    } 

    public void CollectData() 
    { 
     foreach (string address in addresses) 
     { 
      HttpClient client = new HttpClient(); 
      client.BaseAddress = new Uri("http://localhost:1337/"); 
      client.GetAsync(address).ContinueWith(
       getTask => 
        { 
         if (getTask.IsCanceled) 
         { 
          error(); 
         } 
         else if(getTask.IsFaulted) 
         { 
          error(); 
         } 
         else 
         { 
          requestsCompleted++; 
          checkFinished(); 
         } 
        } 
      ); 
     } 
    } 

    public void checkFinished() 
    { 
     if (count == requestsCompleted) 
     { 
      // All data collected 
     } 
    } 

    public void error() 
    { 
     // yes error 
    } 
} 

И это мой контроллер

public ActionResult GetData() 
{ 
    var data = new GetDemData(); 
    // fill data with addresses 
    data.CollectData(); 

    return View(); 
} 

Проблема заключается в том, так как все делается асинхронной, вид сразу вернулся. Как я могу убедиться, что представление возвращается только после сбора всех данных?

+2

Почему вы не используете Await? – Aron

+0

Я довольно новичок в C#, если бы вы могли привести мне пример? Я хочу, чтобы все запросы выполнялись в одно и то же время, если возможно, – user3182508

+1

'await' не заставляет блокировать вызывающий поток. Это просто компилятор, чтобы заставить переписать метод в продолжение. – Tejs

ответ

1
public class GetDemData 
{ 

    List<string> addresses = new List<string>(); 

    public void AddDataToBeCollected(string address) 
    { 
     adresses.Add(address); 
    } 

    public Task CollectData() 
    { 
       var webclient = new WebClient(); 
       var tasks = from address in addresses 
          select webclient.DownloadStringTaskAsync(address); 

       return Task.WhenAll(tasks.Select(
          async (downloadTask) => 
          { 
           var result = await downloadTask; 
           //Do somthing with result 
          })); 
    } 

} 


public async Task<ActionResult> GetData() 
{ 
    var data = new GetDemData(); 
    // fill data with addresses 
    await data.CollectData(); 

    return View(); 
} 
+0

Вы не можете использовать 'Select' для создания асинхронной лямбды. (Здесь вам даже не нужна асинхронная лямбда, просто выберите сама задачу и разверните вызов 'WhenAll'.) Тип делегата не может быть выведен.Кроме того, это 'DownloadStringTaskAsync' не' DownloadStringAsyncTask' – Servy

+0

@Servy, вам нужно что-то сделать с результатом. PS http://stackoverflow.com/questions/10712655/projection-using-async-delegate-lambda – Aron

+0

Ему нужно что-то сделать, когда они все закончили, а не когда каждая операция завершается, что означает ожидание или добавление продолжения, результат 'WhenAll'. Если у него была какая-то работа после каждой загруженной загрузки, то уверен (хотя вы все еще не делаете это правильно), но он этого не делает. – Servy

0

Вам необходимо сохранить ссылку на экземпляры Task, а затем вызвать .Wait() в этой задаче, чтобы принудительно заблокировать поток до тех пор, пока эти запросы не будут выполнены. Например:

+1

Это синхронно ждет всех асинхронных операций. Вы не должны этого делать. Либо код должен быть полностью асинхронным сверху вниз, либо он должен просто использовать синхронные операции до конца. – Servy

+0

Правильно, но ОП спрашивал, как ждать на потоках. Из его примера он не использует MVC Async Controllers. – Tejs

+1

ОП, возможно, попросил об этом, но это все еще не является правильным решением проблемы. Он не знает лучше, поэтому объясните, почему он не должен этого делать. – Servy

0

Вы не можете использовать простой асинхронный/ожидающий, если хотите сразу запустить все запросы. например, если вы использовали await внутри своей петли, он по-прежнему запускал бы один запрос за раз, дождитесь его завершения, затем запустит следующий запрос. Вместо этого вам нужно запустить все запросы, собрать полученные объекты Task и дождаться завершения их всех.

public Task CollectData() 
{ 
    var tasks = new List<Task>(); 
    foreach (string address in addresses) 
    { 
     HttpClient client = new HttpClient(); 
     client.BaseAddress = new Uri("http://example.com:1337/"); 

     //Note we're collecting the resulting Task objects here 
     //We're actually getting the task from the continuation, which is a little bit weird 
     //Alternatively, you could break this into another method that uses await internally 
     var task = client.GetAsync(address).ContinueWith(
      getTask => 
       { 
        if (getTask.IsCanceled) 
        { 
         error(); 
        } 
        else if(getTask.IsFaulted) 
        { 
         error(); 
        } 
        else 
        { 
         requestsCompleted++; 
         checkFinished(); 
        } 
       } 
     ); 
     tasks.Add(task); 
    } 

    //Return a single task that completes when all the subtasks are done 
    return Task.WhenAll(tasks.ToArray()); 
} 

контроллер ...

public async Task<ActionResult> GetData() 
{ 
    var data = new GetDemData(); 
    // fill data with addresses 
    var task = data.CollectData(); 
    await task; 
    return View(); 
} 

Update, несколько чище способ сделать это, используя отдельный метод, который стреляет только один запрос. Кроме того, я просто заметил, что вы не используете HttpClient правильно - это IDisposable, а также содержит внутренний пул запросов, поэтому его следует использовать повторно, а не создавать и уничтожать по запросу.

public async Task CollectData() 
{ 
    var tasks = new List<Task>(); 
    using (var client = new HttpClient()) 
    { 
     foreach (string address in addresses) 
     { 
      tasks.Add(ExecuteSingleRequest(client, address)); 
     } 

     await Task.WhenAll(tasks.ToArray()); 
    } 
} 

private async Task ExecuteSingleRequest(HttpClient client, Uri uri) 
{ 
    try 
    { 
     var response = await client.GetAsync(uri); 
    } 
    catch (Exception ex) 
    { 
     //This is lazy example code, do real error handling here and don't catch Exception 
    } 
} 
+1

Erm ... вы действительно должны использовать async'ness для обработки ошибок. – Aron

+0

Я просто копировал то, что у него было там, так как я действительно не знаю, что он пытается выполнить при обработке ошибок. Но я обновил версию, которая сделает это. –

Смежные вопросы