2015-01-07 4 views
2

У меня есть надстройка Excel, написанная на C#, .NET 4.5. Он отправит множество запросов веб-сервисов на веб-сервер для получения данных. Например. он отправляет 30 000 запросов на сервер веб-сервисов. Когда данные запроса возвращаются, addin будет отображать данные в Excel. Первоначально я выполнил все запросы асинхронно, но когда-нибудь я получу OutOfMemoryException Итак, я изменил, отправил запросы один за другим, но он слишком медленный, требуется много времени для завершения всех запросов. Интересно, есть ли способ, чтобы я мог выполнять 100 запросов одновременно асинхронно, как только данные всех 100 запросов возвращаются и отображаются в Excel, а затем посылают следующие 100 запросов.ограничение количества запросов веб-службы одновременно

Благодаря

Редактировать
На моем надстройке, есть кнопка ленты «Обновить», когда она нажата, начинается процесс обновления.

На главную UI нить, лента/кнопка нажата, она будет вызывать веб-службы BuildMetaData, когда он возвращается обратно, в обратном вызове MetaDataCompleteCallback, другой вызов веб-службы отправляется
Как только он возвращается назад, в его callback DataRequestJobFinished, он будет вызывать график для построения данных в Excel. смотрите ниже

RefreshBtn_Click() 
{ 
    if (cells == null) return; 
    Range firstOccurence = null; 

    firstOccurence = cells.Find(functionPattern, null, 
      null, null, 
      XlSearchOrder.xlByRows, 
      XlSearchDirection.xlNext, 
      null, null, null); 

    DataRequest request = null; 
    _reportObj = null; 
    Range currentOccurence = null; 

    while (!Helper.RefreshCancelled) 
    {         
     if(firstOccurence == null ||IsRangeEqual(firstOccurence, currentOccurence)) break; 

     found = true; 

currentOccurence = cells.FindNext(currentOccurence ?? firstOccurence); 

     try 
     { 
      var excelFormulaCell = new ExcelFormulaCell(currentOccurence); 

      if (excelFormulaCell.HasValidFormulaCell) 
      {                          
       request = new DataRequest(_unityContainer, XLApp, excelFormulaCell); 

       request.IsRefreshClicked = true; 
       request.Workbook = Workbook; 
       request.Worksheets = Worksheets; 

       _reportObj = new ReportBuilder(_unityContainer, XLApp, request, index, false); 
       _reportObj.ParseParameters(); 

       _reportObj.GenerateReport(); 
       //this is necessary b/c error message is wrapped in valid object DataResponse 
       //if (!string.IsNullOrEmpty(_reportObj.ErrorMessage)) //Clear previous error message 
       { 
        ErrorMessage = _reportObj.ErrorMessage; 
        Errors.Add(ErrorMessage); 
        AddCommentToCell(_reportObj); 
        Errors.Remove(ErrorMessage); 
       } 
      }      
     } 
     catch (Exception ex) 
     { 
      ErrorMessage = ex.Message; 
      Errors.Add(ErrorMessage); 
      _reportObj.ErrorMessage = ErrorMessage; 
      AddCommentToCell(_reportObj); 
      Errors.Remove(ErrorMessage); 
      Helper.LogError(ex); 
     } 
    } 

} 

по классу к GenerateReport

public void GenerateReport() 
{ 
    Request.ParseFunction(); 
    Request.MetacompleteCallBack = MetaDataCompleteCallback; 
    Request.BuildMetaData(); 
} 

public void MetaDataCompleteCallback(int id) 
{ 
    try 
    { 
     if (Request.IsRequestCancelled) 
     { 
      Request.FormulaCell.Dispose(); 
      return; 
     } 

     ErrorMessage = Request.ErrorMessage; 
     if (string.IsNullOrEmpty(Request.ErrorMessage)) 
     { 
      _queryJob = new DataQueryJob(UnityContainer, Request.BuildQueryString(), DataRequestJobFinished, Request); 
     } 
     else 
     { 
      ModifyCommentOnFormulaCellPublishRefreshEvent(); 
     } 
    } 
    catch (Exception ex) 
    { 
     ErrorMessage = ex.Message; 
     ModifyCommentOnFormulaCellPublishRefreshEvent(); 
    } 
    finally 
    { 
     Request.MetacompleteCallBack = null; 
    } 
} 


public void DataRequestJobFinished(DataRequestResponse response) 
{ 
    Dispatcher.Invoke(new Action<DataRequestResponse>(DataRequestJobFinishedUI), response); 
} 

public void DataRequestJobFinished(DataRequestResponse response) 
{ 
    try 
    { 
     if (Request.IsRequestCancelled) 
     { 
      return; 
     } 

     if (response.status != Status.COMPLETE) 
     { 
      ErrorMessage = ManipulateStatusMsg(response); 
     } 
     else // COMPLETE 
     { 
      var tmpReq = Request as DataRequest; 
      if (tmpReq == null) return; 

      new VerticalTemplate(tmpReq, response).Plot(); 

     } 
    } 
    catch (Exception e) 
    { 
     ErrorMessage = e.Message; 
     Helper.LogError(e); 
    } 
    finally 
    { 
     //if (token != null) 
     // this.UnityContainer.Resolve<IEventAggregator>().GetEvent<DataQueryJobComplete>().Unsubscribe(token); 
     ModifyCommentOnFormulaCellPublishRefreshEvent(); 
     Request.FormulaCell.Dispose(); 
    } 
} 

на классе участка

public void Plot() 
{ 
... 
    attributeRange.Value2 = headerArray; 
    DataRange.Value2 = .... 
    DataRange.NumberFormat = ... 
} 
+2

Любой минималистский код, чтобы показать вашу проблему? –

+0

Несомненно. Позвольте мне попытаться свести код. спасибо – toosensitive

+3

Вы пробовали использовать Задачи []? Вы можете ограничить количество задач до 100 и выполнить WaitAll, после ожидания вы можете построить данные, а затем снова запустить еще 100. –

ответ

1

У меня много проблем синтаксического анализа кода, чтобы выяснить, является итерируемым для вашего запроса но основной шаблон для асинхронного дозирования будет примерно таким:

static const int batchSize = 100; 
public async Task<IEnumerable<Results>> GetDataInBatches(IEnumerable<RequestParameters> parameters) { 
    if(!parameters.Any()) 
     return Enumerable.Empty<Result>(); 
    var batchResults = await Task.WhenAll(parameters.Take(batchSize).Select(doQuery)); 
    return batchResults.Concat(await GetDataInBatches(parameters.Skip(batchSize));   
} 

где doQuery что-то с подписью

Task<Results> async doQuery(RequestParameters parameters) { 
    //.. however you do the query 
} 

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

Обратите внимание, что это также предполагает, что ваши данные запроса данных выполняются асинхронно и возвращает Task. Большинство библиотек были обновлены для этого (ищите методы с суффиксом Async). Если он не раскрывает этот api, вы можете создать отдельный вопрос о том, как конкретно заставить вашу библиотеку хорошо играть с TPL.

3

OutOfMemoryException не касается слишком большого количества запросов, отправленных одновременно. Речь идет о том, чтобы освободить ваши ресурсы правильно. В моей практике есть две основные проблемы, когда вы получать такое исключение:

  • Неправильно работающие с неизменными структурами или System.String класса
  • Не выбрасывайте ваши располагаемые ресурсы, особенно графические объекты и WCF запросы.

В случае отчетности, по моему мнению, у вас есть второй тип проблемы. DataRequest и DataRequestResponse - хороший момент для начала расследования таких объектов.

Если это не помогает, попробуйте использовать Tasks библиотеки async/await шаблона, вы можете найти хорошие примеры here:

// Signature specifies Task<TResult> 
async Task<int> TaskOfTResult_MethodAsync() 
{ 
    int hours; 
    // . . . 
    // Return statement specifies an integer result. 
    return hours; 
} 

// Calls to TaskOfTResult_MethodAsync 
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync(); 
int intResult = await returnedTaskTResult; 
// or, in a single statement 
int intResult = await TaskOfTResult_MethodAsync(); 


// Signature specifies Task 
async Task Task_MethodAsync() 
{ 
    // . . . 
    // The method has no return statement. 
} 

// Calls to Task_MethodAsync 
Task returnedTask = Task_MethodAsync(); 
await returnedTask; 
// or, in a single statement 
await Task_MethodAsync(); 

В своем коде я вижу while петли, в которой вы можете хранить ваш Task[] размера 100, для которого вы можете использовать WaitAll method, и проблема должна быть решена. Извините, но ваш код достаточно велик, и я не могу предоставить вам более прямой пример.

+0

Хороший вопрос о других причинах 'OutOfMemoryException' –

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