[英]limit # of web service requests simultaneously
我有一個用C#、. NET 4.5編寫的Excel加載項。 它將向Web服務器發送許多Web服務請求以獲取數據。 例如,它將30,000個請求發送到Web服務服務器。 當請求的數據返回時,插件將在Excel中繪制數據。 最初,我異步地完成了所有請求,但是有時我會得到OutOfMemoryException,所以我進行了更改,一個接一個地發送了請求,但是它太慢了,需要很長時間才能完成所有請求。 我想知道是否有一種方法可以一次異步處理100個請求,一旦所有100個請求的數據返回並在Excel中繪圖,然后發送下一個100個請求。
謝謝
編輯
在我的插件上,有一個功能區按鈕“刷新”,單擊該按鈕將開始刷新過程。
在主UI線程上,單擊功能區/按鈕,它將調用Web服務BuildMetaData,一旦返回,在其回調MetaDataCompleteCallback中,將發送另一個Web服務調用
一旦返回,在其回調DataRequestJobFinished中,它將調用plot以在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 = ...
}
OutOfMemoryException
與同時發送太多請求無關。 這是關於以正確的方式釋放資源。 在我的實踐中,遇到此類異常時有兩個主要問題:
System.String
類時出錯 就報告而言,我認為您遇到了第二類問題。 DataRequest
和DataRequestResponse
是開始調查此類對象的好地方。
如果這樣做沒有幫助,請嘗試以async/await
模式使用Tasks
庫,您可以在此處找到良好的示例:
// 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
循環,您可以在其中存儲大小為100
Task[]
,可以使用其中的WaitAll
方法 ,該問題應該得到解決。 抱歉,但是您的代碼足夠多,我無法提供更直接的示例。
我在解析您的代碼以發現正在為您的請求進行迭代時遇到很多麻煩,但是異步批處理的基本模板將是這樣的:
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配合使用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.