How the async Task return conditionally - c#

Hi am trying to return for an async method task conditionally. Below is the way I tried.
public string DoMessage(MyObj obj)
{
string returnStatus = "Processing...";
var storageAccount = CloudStorageAccount.DevelopmentStorageAccount;
var queueClient = storageAccount.CreateCloudQueueClient();
var queue = queueClient.GetQueueReference(ConfigurationManager.AppSettings["QueueName"]);
if (queue.CreateIfNotExists()) {
}
var msg = CloudQueueMessageExtensions.Serialize(obj);
queue.AddMessage(msg);
//Task processTask = RunMessageProces();
var t = Task.Run(() => RunMessageProces());
t.Wait();
return returnStatus;
}
private async Task<string> RunMessageProces()
{
statusProcess = "Your message successfully inserted in process queue.";
await Task.Run(() => {
lock (_oQueue)
{
if (flagProcessing == true) //return when queue processing alredy started
{
return statusProcess; //Error ..??? how to return
}
flagProcessing = true; //else start processing the queue till there are messages.
}
});
statusProcess = ProcessMyMessage();
return statusProcess;
}
private string ProcessMyMessage() {...}
What I am missing? How to return string in between conditionally under async method that too lie inside await lock anonymous block(?). I do async in task as Do Message is flooded with lot of calls simultaneously due to exposed part of a service.

I am assuming you are asking how to access the returned value from the task.
private async Task<string> RunMessageProces()
{
var statusProcess = "Your message successfully inserted in process queue.";
var retValue = await Task.Run(() =>
{
lock (_oQueue)
{
if (flagProcessing == true) //return when queue processing alredy started
{
return "Error"; // or some such error indicator
}
flagProcessing = true; //else start processing the queue till there are messages.
}
return string.Empty; // return a string here too....
});
// if( retValue == "Error" ) { return "Error" }
statusProcess = ProcessMyMessage();
return statusProcess;
}

Related

Alternatives for Monitor (Wait, PluseAll) in Async Tasks in C#

I implemented Task synchronization using Monitor in C#.
However, I have read Monitor should not be used in asynchronous operation.
In the below code, how do I implement Monitor methods Wait and PulseAll with a construct that works with Task (asynchronous operations).
I have read that SemaphoreSlim.WaitAsync and Release methods can help.
But how do they fit in the below sample where multiple tasks need to wait on a lock object, and releasing the lock wakes up all waiting tasks ?
private bool m_condition = false;
private readonly Object m_lock = new Object();
private async Task<bool> SyncInteralWithPoolingAsync(
SyncDatabase db,
List<EntryUpdateInfo> updateList)
{
List<Task> activeTasks = new List<Task>();
int addedTasks = 0;
int removedTasks = 0;
foreach (EntryUpdateInfo entryUpdateInfo in updateList)
{
Monitor.Enter(m_lock);
//If 5 tasks are waiting in ProcessEntryAsync method
if(m_count >= 5)
{
//Do some batch processing to obtian values to set for adapterEntry.AdapterEntryId in ProcessEntryAsync
//.......
//.......
m_condition = true;
Monitor.PulseAll(m_lock); // Wakes all waiters AFTER lock is released
}
Monitor.Exit(m_lock);
removedTasks += activeTasks.RemoveAll(t => t.IsCompleted);
Task processingTask = Task.Run(
async () =>
{
await this.ProcessEntryAsync(
entryUpdateInfo,
db)
.ContinueWith(this.ProcessEntryCompleteAsync)
.ConfigureAwait(false);
});
activeTasks.Add(processingTask);
addedTasks++;
}
}
private async Task<bool> ProcessEntryAsync(SyncDatabase db, EntryUpdateInfo entryUpdateInfo)
{
SyncEntryAdapterData adapterEntry =
updateInfo.Entry.AdapterEntries.FirstOrDefault(e => e.AdapterId == this.Config.Id);
if (adapterEntry == null)
{
adapterEntry = new SyncEntryAdapterData()
{
SyncEntry = updateInfo.Entry,
AdapterId = this.Config.Id
};
updateInfo.Entry.AdapterEntries.Add(adapterEntry);
}
m_condition = false;
Monitor.Enter(m_lock);
while (!m_condition)
{
m_count++;
Monitor.Wait(m_lock);
}
m_count--;
adapterEntry.AdapterEntryId = .... //Set Value obtained form batch processing
Monitor.Exit(m_lock);
}
private void ProcessEntryCompleteAsync(Task<bool> task, object context)
{
EntryProcessingContext ctx = (EntryProcessingContext)context;
try
{
string message;
if (task.IsCanceled)
{
Logger.Warning("Processing was cancelled");
message = "The change was cancelled during processing";
}
else if (task.Exception != null)
{
Exception ex = task.Exception;
Logger.Warning("Processing failed with {0}: {1}", ex.GetType().FullName, ex.Message);
message = "An error occurred while synchronzing the changed.";
}
else
{
message = "The change was successfully synchronized";
if (task.Result)
{
//Processing
//...
//...
}
}
}
catch (Exception e)
{
Logger.Info(
"Caught an exception while completing entry processing. " + e);
}
finally
{
}
}
Thanks

CancellationToken in Web API not working correctly

I have Web Api which gets CancellationToken from users, the method inside it (DoWork) also get CancellationToken:
[HttpPost]
public async Task<long> GetInfo(CancellationToken cancellationToken)
{
long result = 0;
bool notDone = true;
Task<long> t = Task.Run(async () =>
{
if (cancellationToken.IsCancellationRequested)
cancellationToken.ThrowIfCancellationRequested();
while (notDone && !cancellationToken.IsCancellationRequested)
{
result = await DoWork(cancellationToken);
notDone = false;
}
return result;
}, cancellationToken);
try
{
return await t;
}
catch (AggregateException e)
{
Debug.WriteLine("Exception messages:");
foreach (var ie in e.InnerExceptions)
Debug.WriteLine(" {0}: {1}", ie.GetType().Name, ie.Message);
Debug.WriteLine("\nTask status: {0}", t.Status);
throw;
}
catch (Exception ex)
{
throw;
}
}
private Task<long> DoWork(CancellationToken token)
{
long result = 0;
bool notDone = true;
Task<long> task = Task.Run(() =>
{
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
while (notDone && !token.IsCancellationRequested)
{
Thread.Sleep(8000);
result = 2;
notDone = false;
}
return result;
}, token);
return task;
}
I expect when the user cancels the request it aborts the DoWork method and not continue the function, but after sending an Exception, when "Thread.Sleep" complete, the DoWork method continue.
the user can call the API service like this method "cc" as you can see it cancel after 5 seconds and in the DoWork method "Thread.Sleep" is 9 seconds. the user gets an Exception but the method still running.
private async Task<bool> cc()
{
UriBuilder builder = new UriBuilder("http://localhost:12458/api/Test/GetInfo");
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
HttpClient client = new HttpClient();
System.Threading.CancellationTokenSource s = new System.Threading.CancellationTokenSource();
s.CancelAfter(5000);
try
{
var result = client.PostAsJsonAsync<model1>(builder.ToString(), new model1 { }, s.Token).Result;
string tmp = result.Content.ReadAsStringAsync().Result;
long ApiResult = JsonConvert.DeserializeObject<long>(tmp);
}
catch (TaskCanceledException ex)
{
}
catch (OperationCanceledException ex)
{
}
catch (Exception ex)
{
}
finally
{
s.Dispose();
}
return false;
}
When use Thread.Sleep(8000) actually hold the main thread for 8 seconds and It can't check the cancellation token. you should use Task.Delay with cancellation token like this:
while (notDone && !token.IsCancellationRequested)
{
await Task.Delay(8000, token);
result = 2;
notDone = false;
}
Task.Delay check the cancellation token itself.
Ok, in your code you should process CancellationToken.IsCancellationRequested too. It's not kinda of magic, you should do this work.
public void DoWork(CancellationToken ctsToken)
{
ctsToken.ThrowIfCancellationRequested();
DoSomething();
ctsToken.ThrowIfCancellationRequested();
DoSomethingElse();
// end so on with checking CancellationToken before every part of work
}
And your Task should look like this
Task<long> t = Task.Run(async () =>
{
cancellationToken.ThrowIfCancellationRequested();
result = await DoWork(cancellationToken);
notDone = false;
cancellationToken.ThrowIfCancellationRequested();
return result;
}, cancellationToken);
In my case it was because of fiddler. When I closed the fiddler app, it started working like a charm.

Implementing timeout in async retry operations

I wrote an async method with retry logic. It works just fine, however recently I wanted to add a timeout for each try in case the operation takes too long.
public static async Task<Result> PerformAsync(Func<Task> Delegate,
Func<Exception, bool> FailureCallback = null, int Timeout = 30000,
int Delay = 1000, int Threshold = 10)
{
if (Delegate == null)
{
throw new ArgumentNullException(nameof(Delegate));
}
if (Threshold < 1)
{
throw new ArgumentOutOfRangeException(nameof(Threshold));
}
CancellationTokenSource Source = new CancellationTokenSource();
CancellationToken Token = Source.Token;
bool IsSuccess = false;
for (int Attempt = 0; Attempt <= Threshold && !Source.IsCancellationRequested;
Attempt++)
{
try
{
await Delegate();
Source.Cancel();
IsSuccess = true;
break;
}
catch (Exception E)
{
Exceptions.Add(E);
if (FailureCallback != null)
{
bool IsCanceled =
Application.Current.Dispatcher.Invoke(new Func<bool>(() =>
{
return !FailureCallback(E);
}));
if (IsCanceled)
{
Source.Cancel();
IsSuccess = false;
break;
}
}
}
await Task.Delay(Delay);
}
return new Result(IsSuccess, new AggregateException(Exceptions));
}
I've been trying various solutions all over the web, but for whatever reason I've never managed to set timeout for each try individually.
I tried to do this using Task.WhenAny() with Task.Delay(Timeout), but when I launch my program, FailureCallback is called only once and if another try fails, FailureCallback is not called.
Ok, lets start. First of all, the intended usage of a CancellationToken isn't to cancel locally a loop, that's a waste, a CancellationToken reserves some resources and in your case you can simply usea boolean.
bool IsSuccess = false;
bool IsCancelled = false;
for (int Attempt = 0; Attempt <= Threshold; Attempt++)
{
try
{
await Delegate();
IsSuccess = true;
//You are breaking the for loop, no need to test the boolean
//in the for conditions
break;
}
catch (Exception E)
{
Exceptions.Add(E);
if (FailureCallback != null)
{
IsCancelled = Application.Current.Dispatcher.Invoke(new Func<bool>(() =>
{
return !FailureCallback(E);
}));
//You are breaking the for loop, no need to test the boolean
//in the for conditions
if(IsCancelled)
break;
}
}
await Task.Delay(Delay);
}
//Here you have "IsSuccess" and "IsCancelled" to know what happened in the loop
//If IsCancelled is true the operation was cancelled, if IsSuccess is true
//the operation was success, if both are false the attempt surpased threshold.
Second, you must update your delegate to be cancellable, that's the real intended usage of CancellationToken, make your delegate to expect a CancellationToken and use it properly inside the function.
public static async Task<Result> PerformAsync(Func<CancellationToken, Task> Delegate, //..
//This is an example of the Delegate function
public Task MyDelegateImplemented(CancellationToken Token)
{
//If you have a loop check if it's cancelled in each iteration
while(true)
{
//Throw a TaskCanceledException if the cancellation has been requested
Token.ThrowIfCancellationRequested();
//Now you must propagate the token to any async function
//that accepts it
//Let's suppose you are downloading a web page
HttpClient client;
//...
await client.SendAsync(message, Token)
}
}
Finally, now that your task is cancellable you can implement the timeout like this:
//This is the "try" in your loop
try
{
var tokenSource = new CancellationTokenSource();
var call = Delegate(tokenSource.Token);
var delay = Task.Delay(timeout, tokenSource.Token);
var finishedTask = await Task.WaitAny(new Task[]{ call, delay });
//Here call has finished or delay has finished, one will
//still be running so you need to cancel it
tokenSource.Cancel();
tokenSource.Dispose();
//WaitAny will return the task index that has finished
//so if it's 0 is the call to your function, else it's the timeout
if(finishedTask == 0)
{
IsSuccess = true;
break;
}
else
{
//Task has timed out, handle the retry as you need.
}
}

How can I run many asynchronous methods all together?

I want to run my asynchronous methods GetPlayerCountryData() and GetPlayerTagsData() all together to save time instead of starting the next method only after the previous has completed. But I don't know how to do that.
https://jeremylindsayni.wordpress.com/2019/03/11/using-async-await-and-task-whenall-to-improve-the-overall-speed-of-your-c-code/
I have read this tutorial but I don't know how to use await Task.WhenAll() in my code.
In addition, I want to execute the line AllMethodsCompleted = true; after all my asynchronous methods have been completed successfully. Should I use await Task.WhenAll() in this situation?
How can I only set AllMethodsCompleted = true if all my asynchronous methods completed successfully? Is it possible to find out if (result.Error != null) or an exception occurred in one of the asynchronous methods before setting AllMethodsCompleted = true ?
string PlayerDeviceId = "";
private void RegisterGuestPlayFabAccount()
{
PlayerDeviceId = ReturnMobileID();
var requestIOS = new LoginWithIOSDeviceIDRequest { DeviceId = PlayerDeviceId, CreateAccount = true };
var loginTask = PlayFabClientAPI.LoginWithIOSDeviceIDAsync(requestIOS);
loginTask.ContinueWith(OnPlayFabRegisterGuestAccountComplete);
}
private void OnPlayFabRegisterGuestAccountComplete(Task<PlayFabResult<LoginResult>> task)
{
if (task.Result.Result != null)
{
PlayerAccountDetails();
}
if (task.Result.Error != null)
{
OnPlayFabError(task.Result.Error);
}
}
bool AllMethodsCompleted = false;
public async void PlayerAccountDetails()
{
await GetPlayerCountryData();
await GetPlayerTagsData();
AllMethodsCompleted = true;
}
private async Task GetPlayerTagsData()
{
var resultprofile = await PlayFabServerAPI.GetPlayerTagsAsync(new PlayFab.ServerModels.GetPlayerTagsRequest()
{
PlayFabId = PlayerPlayFabID
});
if (resultprofile.Error != null)
OnPlayFabError(result.Error);
else
{
if ((resultprofile.Result != null) && (resultprofile.Result.Tags.Count() > 0))
CurrentPlayerTag = resultprofile.Result.Tags[0].ToString();
}
}
private async Task GetPlayerCountryData()
{
var resultprofile = await PlayFabClientAPI.GetUserDataAsync(new PlayFab.ClientModels.GetUserDataRequest()
{
PlayFabId = PlayerPlayFabID,
Keys = null
});
if (resultprofile.Error != null)
OnPlayFabError(result.Error);
else
{
if (resultprofile.Result.Data == null || !resultprofile.Result.Data.ContainsKey("Country") || !resultprofile.Result.Data.ContainsKey("City"))
Console.WriteLine("No Country/City");
else
{
PlayerCountry = resultprofile.Result.Data["Country"].Value);
PlayerCity = resultprofile.Result.Data["City"].Value);
}
}
}
public async Task PlayerAccountDetails()
{
var playerCountryData = GetPlayerCountryData());
var playerTagsData = GetPlayerTagsData());
await Task.WhenAll(playerCountryData, playerTagsData);
AllMethodsCompleted = true;
}
Here is the method you are in query about and running the 2 methods in async waiting for each to finish then moving on. They just needed to be assigned to a task variable.

Correct way of starting a forever-running loop with Async

I've an existing code I wrote some time ago, that works but I dislike the fact that the thread I start remains in loop.
This piece of code is a consumer on an IBMMQ code, waiting for messages to be processed.The problem I've is that with the following code
private Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
return Task.Factory.StartNew(() =>
{
ConnectToAccessQueue();
Logger.Debug($"Accessed to the queue {queueName}");
Logger.DebugFormat("Repeating timer started, checking frequency: {checkingFrequency}",
checkingFrequency);
while (!cancellationToken.IsCancellationRequested)
{
Logger.Trace( () => "Listening on queues for new messages");
// isChecking = true;
var mqMsg = new MQMessage();
var mqGetMsgOpts = new MQGetMessageOptions
{ WaitInterval = (int)checkingFrequency.TotalMilliseconds };
// 15 second limit for waiting
mqGetMsgOpts.Options |= MQC.MQGMO_WAIT | MQC.MQGMO_FAIL_IF_QUIESCING |
MQC.MQCNO_RECONNECT_Q_MGR | MQC.MQOO_INPUT_AS_Q_DEF;
try
{
mqQueue.Get(mqMsg, mqGetMsgOpts);
if (string.Compare(mqMsg.Format, MQC.MQFMT_STRING, StringComparison.Ordinal) == 0)
{
var text = mqMsg.ReadString(mqMsg.MessageLength);
Logger.Debug($"Message received : [{text}]");
Message message = new Message { Content = text };
foreach (var observer in observers)
observer.OnNext(message);
}
else
{
Logger.Warn("Non-text message");
}
}
catch (MQException ex)
{
if (ex.Message == MQC.MQRC_NO_MSG_AVAILABLE.ToString())
{
Logger.Trace("No messages available");
//nothing to do, emtpy queue
}
else if (ex.Message == MQC.MQRC_CONNECTION_BROKEN.ToString())
{
Logger.ErrorException("MQ Exception, trying to recconect", ex);
throw new ReconnectException();
}
}
Thread.Sleep(100);
}
},cancellationToken);
}
//Calling method
try
{
string queueManagerName = configuration.GetValue<string>("IBMMQ:QUEUE_MANAGER_NAME");
// var queueManager = new MQQueueManager(queueManagerName,dictionary2);
QueueMonitor monitor = new QueueMonitor(configuration, "IMPORTER_RECEIVER_TEST");
//_subscription = monitor.Subscribe(receiver);
await monitor.StartAsync(cts.Token).ConfigureAwait(false);
}
catch (Exception e)
{
log.Error(e, "Error creating the queue monitor or it's subscription");
}
finally
{
WaitForCancel(cts);
}
The call to await monitor.StartAsync(cts.Token).ConfigureAwait(false); remains pending.
How should I modify my code, so that the call returns and in background the task continue to loop?
Thanks in advance
Here is how you can simplify your code by replacing Thread.Sleep with Task.Delay:
private async Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
while (true)
{
// Process mqQueue here
await Task.Delay(100, cancellationToken);
}
}
Task.Delay has the advantage that accepts a CancellationToken, so in case of cancellation the loop will exit immediately. This could be important if the pooling of the MQ was lazier (for example every 5 seconds).
private static Task _runningTask;
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
_runningTask = ExecuteQueuePolling(cts.Token);
WaitForCancel(cts);
}
private static void WaitForCancel(CancellationTokenSource cts)
{
var spinner = new SpinWait();
while (!cts.IsCancellationRequested
&& _runningTask.Status == TaskStatus.Running) spinner.SpinOnce();
}
private static Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
var t = new Task(() =>
{
while (!cancellationToken.IsCancellationRequested)
; // your code
if (cancellationToken.IsCancellationRequested)
throw new OperationCanceledException();
}, cancellationToken, TaskCreationOptions.LongRunning);
t.Start();
return t;
}

Categories