I have a async task method which will be accessed on both foreground and background. In foreground, where user call this method via a button whereas in the background I have a timer triggering the method in some interval.
I tried put lock but it gives exception await can not be inside the lock statement.
public static async Task<CommonResult<IEnumerable<AttendanceDTO>>> DownloadAttendanceAsync(string selectedDate, int siteId)
{
try
{
if (new AttendanceBLL().IsLocalDataAvailable(siteId, DateTime.Parse(selectedDate)))
return new CommonResult<IEnumerable<AttendanceDTO>>() { IsSuccess = true, Data = null };
var queue = new EntityQueueBLL();
//var latestUpdatedOn = queue.GetMaxUpdated(siteId, EntityIDEnum.Attendance);
var url = string.Format(Constants.PathPullAttendance, selectedDate, siteId);
var result = await new HttpRequestClient().GetRequest<IEnumerable<AttendanceDTO>>(url);
if (!result.IsSuccess)
return new CommonResult<IEnumerable<AttendanceDTO>>() { IsSuccess = false, Data = null, ErrorMessage = result.ErrorMessage };
if (result.Data != null && result.Data.Count() > 0)
{
var maxUpdatedOn = result.Data.Max(x => DateTime.Parse(x.UpdatedOn));
queue.Add(siteId, result.Data, result.Data.Count(), EntityIDEnum.Attendance, EntityType.Attendance, maxUpdatedOn);
}
return new CommonResult<IEnumerable<AttendanceDTO>> { IsSuccess = true, Data = result.Data };
}
catch (Exception e)
{
return new CommonResult<IEnumerable<AttendanceDTO>> { IsSuccess = false, Data = null, ErrorMessage = "Download Error" };
}
}
I tried put lock but it gives exception await can not be inside the lock statement.
The await-compatible equivalent of lock is SemaphoreSlim:
private static SempahoreSlim _mutex = new SemaphoreSlim(1);
public static async Task<CommonResult<IEnumerable<AttendanceDTO>>> DownloadAttendanceAsync(string selectedDate, int siteId)
{
await _mutex.WaitAsync();
try
{
...
}
catch (Exception e)
{
return new CommonResult<IEnumerable<AttendanceDTO>> { IsSuccess = false, Data = null, ErrorMessage = "Download Error" };
}
finally
{
_mutex.Release();
}
}
I use
AsyncLazy
For my initialization, which can be called from different threads. The task is only called once and other callers wait for it.
try the below:
await Task.Delay(2000);
Related
I am calling WCF service in ASP.NET Core and everything is working fine, but whenever end of using gets executed, I get an error:
This OperationContextScope is being disposed out of order
I believe I am using wrong pattern to call WCF service using async/await but I am not sure what I am doing wrong.
Below is the code I am using to call a service.
[HttpPost]
public async Task<IActionResult> Runcase(IFormCollection formCollection)
{
if (ModelState.IsValid)
{
var runnumber = formCollection["Run number"];
await CallServiceasync();
return RedirectToAction("", "");
}
else
{
return View(formCollection);
}
}
public async Task CallServiceasync()
{
var product = p1.Value;
var a = product.first;
foreach (int Age in a.age)
{
foreach (int Gender in a.sex)
{
foreach (int Healthclass in a.uclass)
{
RequestData requestData = new RequestData()
{
ProductID = 534,
STATE = "CO",
AGE1 = Age,
SEX1 = Gender,
UND_CLASS1 = Healthclass,
};
RecieveResponseasync(requestData);
}
}
}
}
public async Task RecieveResponseasync(InputValues inputValues)
{
string reqedata = "";
string apikey = "001010iZno7001010L";
QuoteEngineService.MarketingSoftwareClient Service = new QuoteEngineService.MarketingSoftwareClient();
await Service.OpenAsync();
try
{
using (OperationContextScope scope = new OperationContextScope(Service.InnerChannel))
{
HttpRequestMessageProperty httpRequestMessage = new HttpRequestMessageProperty();
httpRequestMessage.Headers.Add("apikey", apikey);
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestMessage;
reqedata = inputValues.XmlSerializetoString();
var result = await Service.ProcessRequestAsync(reqedata, "4fa2-ae27");
var outputvalues = new OutputvaluesViewModel();
outputvalues = result.DeserializeToObject();
List<OutputValue> outputs = new List<OutputValue>();
if (outputvalues.InitialPremium != null)
outputs.Add(new OutputValue { Name = "InitialPremium", Result = outputvalues.InitialPremium});
if (outputvalues.TargetPremium != null)
outputs.Add(new OutputValue { Name = "TargetPremium", Result = outputvalues.TargetPremium });
foreach (var output in outputs)
{
await _context.outputValues.AddAsync(output);
await _context.SaveChangesAsync();
}
await Task.Delay(500);
}
}// **At this point I am getting error**
catch (Exception ex)
{
throw;
}
finally
{
if (Service.State == System.ServiceModel.CommunicationState.Opened)
{
await Service.CloseAsync();
}
}
}
From the docs:
Warning
Do not use the asynchronous "await" pattern within a OperationContextScope block. When the continuation occurs, it may run on a different thread and OperationContextScope is thread specific. If you need to call "await" for an async call, use it outside of the OperationContextScope block.
Please correct me if I have some errors in this logic (not some elegancy things like getting rid of constructor initialization and using Init method instead for Poll). I have not had experience with timer callbacks so far. The code is pretty self-explanatory, I hope. What confuses me a bit is some mix of async things (like connection client creation) and further code - though, I just reused IClient class, it's not mine):
public async Task<WaitForLoanActivationDto> WaitForLoanActivation(string userName, string accountGuid, int timeout)
{
const int dueTime = 0;
const int pollPeriod = 500;
Poll<WaitForLoanActivationDto> state = new Poll<WaitForLoanActivationDto>
{
Client = await _rpcConnectionPool.GetClientAsync(userName),
AutoResetEvent = new AutoResetEvent(false),
StartTime = DateTime.Now,
Timeout = timeout,
Parameters = new Variant[] { accountGuid },
Result = new WaitForLoanActivationDto { Active = false }
};
Timer timer = new Timer(new TimerCallback(WaitForLoanActivationCallback), state, dueTime, pollPeriod);
state.AutoResetEvent.WaitOne();
timer.Dispose(state.AutoResetEvent);
if (state.ThreadException != null)
{
throw state.ThreadException;
}
return state.Result;
}
private void WaitForLoanActivationCallback(object state)
{
Poll<WaitForLoanActivationDto> pollState = (Poll<WaitForLoanActivationDto>)state;
if (pollState.StartTime.AddMilliseconds(pollState.Timeout) >= DateTime.Now)
{
try
{
using (RPCReply reply = ResultHelper.Check(pollState.Client.ExecuteRemoteCommand(WaitForLoanActivationRpcName, pollState.Parameters)))
{
pollState.Result.Active = reply[2].IDList["Active"].AsBoolean().Value;
VariantList statusList = reply[2].IDList["statuses"].List;
if (statusList.Count > 0)
{
var statuses = CustomerInformationConverter.GetStatusesList(statusList);
pollState.Result.Statuses = statuses.ToArray();
}
if (pollState.Result.Active)
{
pollState.AutoResetEvent.Set();
}
}
}
catch (Exception ex)
{
pollState.Result = null;
pollState.ThreadException = ex;
pollState.AutoResetEvent.Set();
}
}
else
{
pollState.AutoResetEvent.Set();
}
}
Thank you guys.
#ckuri, based on your idea I came up with the code below. I have not used Task.Delay though, because as I understood it creates delay even if the task is successfully complete - after it. The objective of my case is to run RPC method each pollPeriod milliseconds during timeout milliseconds. It the method returns Active == false - keep polling, otherwise - return the result. RPC execution time might take more than pollPeriod milliseconds, so if it's already running - no sense to spawn another task.
public async Task<WaitForLoanActivationDto> WaitForLoanActivation(string userName, string accountGuid, int timeout)
{
var cancellationTokenSource = new CancellationTokenSource();
try
{
const int pollPeriod = 500;
IClient client = await _rpcConnectionPool.GetClientAsync(userName);
DateTime startTime = DateTime.Now;
WaitForLoanActivationDto waitForLoanActivationDto = null;
while (startTime.AddMilliseconds(timeout) >= DateTime.Now)
{
waitForLoanActivationDto = await Task.Run(() => WaitForLoanActivationCallback(client, accountGuid), cancellationTokenSource.Token);
if (waitForLoanActivationDto.Active)
{
break;
}
else
{
await Task.Delay(pollPeriod, cancellationTokenSource.Token);
}
}
return waitForLoanActivationDto;
}
catch (AggregateException ex)
{
cancellationTokenSource.Cancel();
throw ex.InnerException;
}
}
private WaitForLoanActivationDto WaitForLoanActivationCallback(IClient client, string accountGuid)
{
using (RPCReply reply = ResultHelper.Check(client.ExecuteRemoteCommand(WaitForLoanActivationRpcName, accountGuid)))
{
var waitForLoanActivationDto = new WaitForLoanActivationDto
{
Active = reply[2].IDList["Active"].AsBoolean().Value
};
VariantList statusList = reply[2].IDList["statuses"].List;
if (statusList.Count > 0)
{
var statuses = CustomerInformationConverter.GetStatusesList(statusList);
waitForLoanActivationDto.Statuses = statuses.ToArray();
}
return waitForLoanActivationDto;
}
}
The order of async-await operations in this bit of code is a bit off. The await Task inside DownloadMetasBySortAsync isn't behaving like I thought it should. The task completes itself, but only after DownloadMetasBySortAsync has already returned null to DownloadMetasAsync.
I've tried adding Task.Wait() on the tasks in DownloadMetasAsync and the offending, awaited task. I tried ConfigureAwait(). DownloadMetasBySortAsync always kicks back null before the needed task has been finished.
What am I missing?
public async Task<List<MyMeta>> DownloadMetasAsync(SortType sortType)
{
ResetFlags();
_cloudMetas = await DownloadMetasBySortAsync(sortType);
Debug.Log((_cloudMetas != null) ? _cloudMetas.Count + " metas downloaded" : "NULL cloudmetas");
return _cloudMetas;
}
private async Task<List<MyMeta>> DownloadMetasBySortAsync(SortType sortType)
{
//Load Table
Table dbTable = null;
//Can't call LoadTable from main thread
await Task.Run(() => {
try {
dbTable = Table.LoadTable(DBClient, new TableConfig(_databaseName));
}catch( Exception ex ) {
_operationFailed = true;
ThrowError(ex);
}
});
if(dbTable == null ) { return null; }
//Set up secondary local index if needed
string sortIndex = (sortType == SortType.Creator) ? "date-creator-index" : null;
return await ScanTable(_dbName, sortIndex); //Scan table for list of metas
}
private async Task<List<MyMeta>> ScanTable(string dbName, string index)
{
List<MyMeta> metaList = null;
try {
Dictionary<string,AttributeValue> lastKeyEvaluated = null;
do {
var request = new ScanRequest
{
TableName = dbName,
IndexName = index,
ConsistentRead = true,
ExclusiveStartKey = lastKeyEvaluated
};
Debug.Log("Scanning...");
await Task.Run(() =>
{
DBClient.ScanAsync(request, (responseObj) =>
{
if( responseObj == null ) {
Debug.LogWarning("Response NULL");
}
else {
Debug.Log("Response received");
if(metaList == null ) { metaList = new List<MyMeta>(); }
var resultList = ProcessScanResult(responseObj.Response);
if( resultList != null && resultList.Count > 0 ) {
metaList.AddRange(resultList);
}
}
lastKeyEvaluated = responseObj.Response.LastEvaluatedKey;
});
});
} while( lastKeyEvaluated != null && lastKeyEvaluated.Count != 0 );
}
catch( Exception ex ) {
ThrowError(ex);
}
return metaList;
}
Since the DBClient.ScanAsync is not an async/await method but a callback type of async method it will return immediately after being called and the results will only come later. To handle this you can use TaskCompletionSource to make it into awaitable thing:
var task = new TaskCompletionSource<responseObjType>();
DBClient.ScanAsync(request, (responseObj) =>
{
task.SetResult(responseObj);
}
// Will wait for the callback to be called before continuing and get the results
var responseObj = await task.Task;
if( responseObj == null )
{
...
This way the method will wait until the callback is called, the data is sent back via the TaskCompletionSource object and your code can process it further. This assumes you don't specifically want to run the rest of the code inside the callback, for threading purposes or anything, and will return to the main code flow to do the rest. You can also do the processing in the callback if you want.
I have written an app that goes through our own properties and scraps the data. To make sure I don't run through the same URLs, I am using a MySQL database to store the URL, flag it once its processed. All this was being done in a single thread and it's fine if I had only few thousand entries. But I have few hundred thousand entries that I need to parse so I need to make changes in the code (I am newbie in multithreading in general). I found an example and was trying to copy the style but doesn't seem to work. Anyone know what the issue is with the following code?
EDIT: Sorry didn't mean to make people guess the issue but was stupid of me to include the exception. Here is the exception
"System.InValidCastException: 'Specified cast is not valid.'"
When I start the process it collects the URLs from the database and then never hits DoWork method
//This will get the entries from the database
List<Mappings> items = bot.GetUrlsToProcess(100);
if (items != null)
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Worker.Done = new Worker.DoneDelegate(WorkerDone);
foreach (var item in items)
{
urls.Add(item.Url);
WaitingTasks.Enqueue(new Task(id => new Worker().DoWork((int)id, item.Url, token), item.Url, token));
}
LaunchTasks();
}
static async void LaunchTasks()
{
// keep checking until we're done
while ((WaitingTasks.Count > 0) || (RunningTasks.Count > 0))
{
// launch tasks when there's room
while ((WaitingTasks.Count > 0) && (RunningTasks.Count < MaxRunningTasks))
{
Task task = WaitingTasks.Dequeue();
lock (RunningTasks) RunningTasks.Add((int)task.AsyncState, task);
task.Start();
}
UpdateConsole();
await Task.Delay(300); // wait before checking again
}
UpdateConsole(); // all done
}
static void UpdateConsole()
{
Console.Write(string.Format("\rwaiting: {0,3:##0} running: {1,3:##0} ", WaitingTasks.Count, RunningTasks.Count));
}
static void WorkerDone(int id)
{
lock (RunningTasks) RunningTasks.Remove(id);
}
public class Worker
{
public delegate void DoneDelegate(int taskId);
public static DoneDelegate Done { private get; set; }
public async void DoWork(object id, string url, CancellationToken token)
{
if (token.IsCancellationRequested) return;
Content obj;
try
{
int tries = 0;
bool IsUrlProcessed = true;
DateTime dtStart = DateTime.Now;
string articleDate = string.Empty;
try
{
ScrapeWeb bot = new ScrapeWeb();
SearchApi searchApi = new SearchApi();
SearchHits searchHits = searchApi.Url(url, 5, 0);
if (searchHits.Hits.Count() == 0)
{
obj = await bot.ReturnArticleObject(url);
if (obj.Code != HttpStatusCode.OK)
{
Console.WriteLine(string.Format("\r Status is {0}", obj.Code));
tries = itemfound.UrlMaxTries + 1;
IsUrlProcessed = false;
itemfound.HttpCode = obj.Code;
}
else
{
string title = obj.Title;
string content = obj.Contents;
string description = obj.Description;
Articles article = new Articles();
article.Site = url.GetSite();
article.Content = content;
article.Title = title;
article.Url = url.ToLower();
article.Description = description;
string strThumbNail = HtmlHelper.GetImageUrl(url, obj.RawResponse);
article.Author = HtmlHelper.GetAuthor(url, obj.RawResponse);
if (!string.IsNullOrEmpty(strThumbNail))
{
//This condition needs to be added to remove ?n=<number> from EP thumbnails
if (strThumbNail.Contains("?"))
{
article.ImageUrl = strThumbNail.Substring(0, strThumbNail.IndexOf("?")).Replace("http:", "https:");
}
else
article.ImageUrl = strThumbNail.Replace("http:", "https:");
}
else
{
article.ImageUrl = string.IsNullOrEmpty(strThumbNail) ? article.Url.GetDefaultImageUrls() : strThumbNail.Replace("http:", "https:");
}
articleDate = HtmlHelper.GetPublishDate(url, obj.RawResponse);
if (string.IsNullOrEmpty(articleDate))
article.Pubdate = DateTime.Now;
else
article.Pubdate = DateTime.Parse(articleDate);
var client = new Index(searchApi);
var result = client.Upsert(article);
itemfound.HttpCode = obj.Code;
if (result)
{
itemfound.DateCreated = DateTime.Parse(articleDate);
itemfound.DateModified = DateTime.Parse(articleDate);
UpdateItem(itemfound);
}
else
{
tries = itemfound.UrlMaxTries + 1;
IsUrlProcessed = false;
itemfound.DateCreated = DateTime.Parse(articleDate);
itemfound.DateModified = DateTime.Parse(articleDate) == null ? DateTime.Now : DateTime.Parse(articleDate);
UpdateItem(itemfound, tries, IsUrlProcessed);
}
}
}
else
{
tries = itemfound.UrlMaxTries + 1;
IsUrlProcessed = true;
itemfound.HttpCode = HttpStatusCode.OK;
itemfound.DateCreated = DateTime.Parse(articleDate);
itemfound.DateModified = DateTime.Parse(articleDate) == null ? DateTime.Now : DateTime.Parse(articleDate);
}
}
catch (Exception e)
{
tries = itemfound.UrlMaxTries + 1;
IsUrlProcessed = false;
itemfound.DateCreated = DateTime.Parse(articleDate);
itemfound.DateModified = DateTime.Parse(articleDate) == null ? DateTime.Now : DateTime.Parse(articleDate);
}
finally
{
DateTime dtEnd = DateTime.Now;
Console.WriteLine(string.Format("\r Total time taken to process items is {0}", (dtEnd - dtStart).TotalSeconds));
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
Done((int)id);
}
}
All this code is based from Best multi-thread approach for multiple web requests this link. Can someone tell me how to get this approach running?
I think the problem is in the way you're creating your tasks:
new Task(id => new Worker().DoWork((int)id, item.Url, token), item.Url, token)
This Task constructor overload expected Action<object> delegate. That means id will be typed as object and you need to cast it back to something useful first.
Parameters
action
Type: System.Action<Object>
The delegate that represents the code to execute in the task.
state
Type: System.Object
An object representing data to be used by the action.
cancellationToken
Type: System.Threading.CancellationToken
-The CancellationToken that that the new task will observe.
You decided to cast it to int by calling (int)id, but you're passing item.Url as the object itself. I can't tell you 100% what the type of Url is but I don't expect Url-named property to be of type int.
Based on what #MarcinJuraszek said I just went back to my code and added an int as I couldn't find another way to resolve it. Here is the change I made
int i=0
foreach (var item in items)
{
urls.Add(item.Url);
WaitingTasks.Enqueue(new Task(id => new Worker().DoWork((string)id, item.Url, token), item.Url, token));
i++;
}
I have client code (ViewModeL) that is calling a ConnectedThingService that is calling a CoapService to update the mode on a "thing". The update method on the CoapService returns a task and has parameters for success and error responses.
Task<Task> SetModeFrom(DeviceModel Device, CoAPResponseReceivedHandler responseHandler, CoAPErrorHandler errorHandler);```
CoAPResponseRecivedHandler and CoAPErrorHandler are delegates.
Here is the code that calls the CoapService:
public async Task<ApiResponse> UpdateModeAsync(int mode)
{
ApiResponse result = new ApiResponse();
await _coapService.SetControlModeFromCombo(device, (updateResp) =>
{
if (updateResp.Code.Value != CoAPMessageCode.CONTENT)
{
result.IsSuccessful = false;
result.ErrorMessages.Add("Failed setting mode");
}
else
{
var coapResponse =
JsonConvert.DeserializeObject<ControlModel>(updateResp.GetPayload());
result.IsSuccessful = true;
Mode = mode;
}
}, (e, associatedMsg) =>
{
result.IsSuccessful = false;
result.ErrorMessages.Add("Failed setting mode");
});
return result;
The problem is the call is returning before either of the delegates are firing and therefore I'm always returning false (the default) in the ApiResponse. Is there a way to make this call and wait for either of the 2 delegates to get called before returning back to the client, and do so in a way that is not blocking? And is it possible to have a timeout so that if neither of the 2 delegates fire after a timeout period, it returns false?
Edit: I inherited this code. The lowest level code is from CoAPSharp.com. Here is the code for SetControlModeFromCombo:
public async Task<Task> SetControlModeFromCombo(DeviceModel Device,
CoAPResponseReceivedHandler responseHandler, CoAPErrorHandler
errorHandler)
{
ushort msgID = Convert.ToUInt16(new Random().Next(1000, 9999));
var task = new TaskCompletionSource<DeviceModel>();
if (!string.IsNullOrWhiteSpace(Device.CODE))
{
var controlSettings = new ControlModel()
{
Mode = Device.ComboSettings.Mode,
};
await _coap.MakeRequest(Device.CODE, JsonConvert.SerializeObject(controlSettings.ControlMode()), Device.IPADDRESS, ControlModel.Endpoint, CoAPMessageType.CON, CoAPMessageCode.PUT, msgID, (CoAPResponse response) =>
{
responseHandler(response);
task.SetResult(Device);
}, (Exception e, AbstractCoAPMessage associatedMsg) =>
{
errorHandler(e, associatedMsg);
});
}
else
{
ThrowMissingCodeError(task);
}
return task.Task;
}
And here is the MakeRequest Method:
public async Task MakeRequest(string token, string outboundIP, string uri, byte type, byte code, ushort id, CoAPResponseReceivedHandler responseHandler, CoAPErrorHandler errorHandler)
{
try
{
_coapClient = new CoAPClientChannel();
await _coapClient.Initialize(outboundIP, _outboundPort);
_coapClient.CoAPResponseReceived += responseHandler;
_coapClient.CoAPError += errorHandler;
await _coapClient.Send(AssembleRequest(type, code, id, outboundIP, uri, token), outboundIP);
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}