I wrote down these lines of code:
public static bool RetryUntilSuccessOrTimeoutAsync(Func<bool> task,
TimeSpan executionTimeout, CancellationToken? token = null) {
var data = new ExecutionContextData(task, executionTimeout, token);
var nonBlockingTask = new Task<bool>(SyncTaskExecutor, data);
nonBlockingTask.Start();
var result = nonBlockingTask.ContinueWith(t => t.Result);
return result.Result;
}
class ExecutionContextData {
private readonly Func<bool> task;
private readonly TimeSpan executionTimeout;
private readonly CancellationToken? cancellationToken;
public ExecutionContextData(Func<bool> task, TimeSpan executionTimeout, CancellationToken? cancellationToken) {
this.cancellationToken = cancellationToken;
this.executionTimeout = executionTimeout;
this.task = task;
}
public Func<bool> Task {
get { return task; }
}
public TimeSpan ExecutionTimeout {
get { return executionTimeout; }
}
public CancellationToken? CancellationToken {
get { return cancellationToken; }
}
}
private static bool SyncTaskExecutor(object executionHelper) {
var context = executionHelper as ExecutionContextData;
Task<bool> newTask =
context.CancellationToken.HasValue ? new Task<bool>(ExecuteTask, context.Task, context.CancellationToken.Value)
: new Task<bool>(ExecuteTask, context.Task);
newTask.Start();
bool timeoutResult = newTask.Wait(context.ExecutionTimeout);
if (timeoutResult)
return newTask.Result;
return false;
}
But as I understand the Result property invokation will block a caller. So, I quite don't understand how to accomplish this task:
"How to execute a task asynchronously, so if timeout is exceeded then it will return false or it will return the result of the task which should be executed over and over again?"
Why not try something like this if you have operations that you may want to cancel or have time out:
public static class Retries
{
public enum Result
{
Success,
Timeout,
Canceled,
}
public static Task<Result> RetryUntilTimedOutOrCanceled(this Func<bool> func, CancellationToken cancel, TimeSpan timeOut)
{
return Task.Factory.StartNew(() =>
{
var start = DateTime.UtcNow;
var end = start + timeOut;
while (true)
{
var now = DateTime.UtcNow;
if (end < now)
return Result.Timeout;
var curTimeOut = end - now;
Task<bool> curTask = null;
try
{
if (cancel.IsCancellationRequested)
return Result.Canceled;
curTask = Task.Factory.StartNew(func, cancel);
curTask.Wait((int)curTimeOut.TotalMilliseconds, cancel);
if (curTask.IsCanceled)
return Result.Canceled;
if (curTask.Result == true)
return Result.Success;
}
catch (TimeoutException)
{
return Result.Timeout;
}
catch (TaskCanceledException)
{
return Result.Canceled;
}
catch (OperationCanceledException)
{
return Result.Canceled;
}
}
});
}
}
class Program
{
static void Main(string[] args)
{
var cancelSource = new CancellationTokenSource();
Func<bool> AllwaysFalse = () => false;
Func<bool> AllwaysTrue = () => true;
var result = AllwaysFalse.RetryUntilTimedOutOrCanceled(cancelSource.Token, TimeSpan.FromSeconds(3)).Result;
Console.WriteLine(result);
result = AllwaysTrue.RetryUntilTimedOutOrCanceled(cancelSource.Token, TimeSpan.FromSeconds(3)).Result;
Console.WriteLine(result);
var rTask = AllwaysFalse.RetryUntilTimedOutOrCanceled(cancelSource.Token, TimeSpan.FromSeconds(100));
System.Threading.Thread.Sleep(1000);
cancelSource.Cancel();
result = rTask.Result;
Console.WriteLine(result);
Console.ReadLine();
}
}
Related
I have a method that returns some value based on an API call, this API limits the amount of calls that you can do per period of time. I need to access the results of this call from multiple threads. Right now i have the following code:
class ReturningSemaphoreLocker<TOutput>
{
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
public async Task<T> LockAsync<T>(Func<Task<T>> worker)
{
await _semaphore.WaitAsync();
try
{
return await worker();
}
finally
{
_semaphore.Release();
}
}
}
Usage example:
...
private static readonly ReturningSemaphoreLocker<List<int>> LockingSemaphore = new ReturningSemaphoreLocker<List<int>>();
...
public async Task<List<int>> GetStuff()
{
return await LockingSemaphore.LockAsync(async () =>
{
var client = _clientFactory.CreateClient("SomeName");
using (var cts = GetDefaultRequestCts())
{
var resp = await client.GetAsync("API TO QUERY URL", cts.Token);
var jsonString = await resp.Content.ReadAsStringAsync();
var items = JsonConvert.DeserializeObject<List<int>>(jsonString);
return items;
}
});
}
So the question is: how do i get the same result from GetStuff() if it's already running WITHOUT querying the API again and query the API again if the method is not running at this very moment?
The trick here is to hold onto the Task<T> that is the incomplete result; consider the following completely untested approach - the _inProgress field is the key here:
private static readonly ReturningSemaphoreLocker<List<int>> LockingSemaphore = new ReturningSemaphoreLocker<List<int>>();
...
private Task<List<int>> _inProgress;
public Task<List<int>> GetStuffAsync()
{
if (_inProgress != null) return _inProgress;
return _inProgress = GetStuffImplAsync();
}
private async Task<List<int>> GetStuffImplAsync()
{
var result = await LockingSemaphore.LockAsync(async () =>
{
var client = _clientFactory.CreateClient("SomeName");
using (var cts = GetDefaultRequestCts())
{
var resp = await client.GetAsync("API TO QUERY URL", cts.Token);
var jsonString = await resp.Content.ReadAsStringAsync();
var items = JsonConvert.DeserializeObject<List<int>>(jsonString);
return items;
}
});
// this is important so that if everything turns
// out to be synchronous, we don't nuke the _inProgress field *before*
// it has actually been set
await Task.Yield();
// and now wipe the field since we know it is no longer in progress;
// the next caller should actually try to do something interesting
_inProgress = null;
return result;
}
Here is a class that you could use for time-based throttling, instead of the ReturningSemaphoreLocker:
class ThrottledOperation
{
private readonly object _locker = new object();
private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
private Task _task;
public Task<T> GetValueAsync<T>(Func<Task<T>> taskFactory, TimeSpan interval)
{
lock (_locker)
{
if (_task != null && (_stopwatch.Elapsed < interval || !_task.IsCompleted))
{
return (Task<T>)_task;
}
_task = taskFactory();
_stopwatch.Restart();
return (Task<T>)_task;
}
}
}
The GetValueAsync method returns the same task, until the throttling interval has been elapsed and the task has been completed. At that point it creates and returns a new task, using the supplied task-factory method.
Usage example:
private static readonly ThrottledOperation _throttledStuff = new ThrottledOperation();
public Task<List<int>> GetStuffAsync()
{
return _throttledStuff.GetValueAsync(async () =>
{
var client = _clientFactory.CreateClient("SomeName");
using (var cts = GetDefaultRequestCts())
{
var resp = await client.GetAsync("API TO QUERY URL", cts.Token);
var jsonString = await resp.Content.ReadAsStringAsync();
var items = JsonConvert.DeserializeObject<List<int>>(jsonString);
return items;
}
}, TimeSpan.FromSeconds(30));
}
var contractSchemaTask = contractSchemaRepository.GetByContractIdAsync(data.Id);
var sectionsTask = sectionRepository.GetAllByContractIdAsync(id);
var latestContractIdTask = contractRepository
.GetLatestContractIdByFolderIdAsync(data.FolderId.Value);
List<Task> allTasks = new List<Task>()
{ contractSchemaTask, sectionsTask, latestContractIdTask };
while (allTasks.Any())
{
Task finished = await Task.WhenAny(allTasks);
if (finished == contractSchemaTask)
{
var contractSchema = await contractSchemaTask;
result.ReturnData.IsSchedules = contractSchema.Count > 0 ? true : false;
}
else if (finished == sectionsTask)
{
List<Section> sections = await sectionsTask;
List<TextValueVM> SectionTabList = sections.Count > 0 ? sections
.OrderBy(a => a.SectionNumber)
.Select(a => new TextValueVM()
{ Text = a.ToString(), Value = a.Id.ToString() })
.ToList() : new List<TextValueVM>();
bool IsSectionsLinked = false;
int linkSectionCount = sections
.Where(x => x.LinkSectionId != null && x.LinkSectionId != Guid.Empty)
.ToList()
.Count();
if (linkSectionCount == 0 && sections.Count > 0)
{
List<Guid> sectionIds = sections.Select(x => x.Id.Value).ToList();
List<Section> currentContractLinkSections = await sectionRepository
.GetSectionsByLinkSectionIdAsync(sectionIds);
if (currentContractLinkSections.Count > 0)
{
IsSectionsLinked = true;
}
}
else if (linkSectionCount > 0)
{
IsSectionsLinked = true;
}
result.ReturnData.SectionTabList = SectionTabList;
result.ReturnData.IsSectionsLinked = IsSectionsLinked;
}
else if (finished == latestContractIdTask)
{
Guid LatestContractId = await latestContractIdTask;
result.ReturnData.isLatestContract
= (data.Id == LatestContractId) ? true : false;
}
allTasks.Remove(finished);
}
I am working on a asp.net core 3.0 WebAPI project. Above is the sample code for independent tasks that I handle using a while loop. Is there any better or efficient approach for handling independent tasks in the asynchronous programming?
P.S: All the 3 tasks are independent and may vary on their response time depending upon the number of records fetched from the database.
You should do it like this:
public Task Main()
{
var result = new Result();
return Task.WhenAll(TaskOne(result), TaskTwo(result), TaskThree(result));
}
private async Task TaskOne(Result result)
{
var contractSchema = await contractSchemaRepository.GetByContractIdAsync(data.Id);
//your logic for task1, set related result properties
}
private async Task TaskTwo(Result result)
{
var sections = await sectionRepository.GetAllByContractIdAsync(id);
//your logic for task2, set related result properties
}
private async Task TaskThree(Result result)
{
var latestContractId = await contractRepository.GetLatestContractIdByFolderIdAsync(data.FolderId.Value);
//your logic for Task3, set related result properties
}
Result class should be implemented as thread-safe because tasks can be executed simultaneously. If you just set different properties in each method it should be OK.
Combining Task.WhenAll with Continue allows you to execute code as soon the task finish without having to await the rest of the tasks.
class Test {
public static async Task Main() {
var t1 = AsyncWork1().ContinueWith((t) => Console.WriteLine($"Task1 finished with value {t.Result}"));
var t2 = AsyncWork2().ContinueWith((t) => Console.WriteLine($"Task2 finished with value {t.Result}"));
var t3 = AsyncWork3().ContinueWith((t) => Console.WriteLine($"Task3 finished with value {t.Result}"));
await Task.WhenAll(new[] { t1, t2, t3 });
//here we know that all tasks has been finished and its result behaviour executed.
Console.ReadKey();
}//main
public static async Task<int> AsyncWork1() {
await Task.Delay(1000);
return 1;
}
public static async Task<string> AsyncWork2() {
await Task.Delay(100);
return "work2";
}
public static async Task<bool> AsyncWork3() {
await Task.Delay(500);
return true;
}
}//class Test
Compare with this:
class Test {
public static async Task Main() {
var t1 = AsyncWork1();
var t2 = AsyncWork2();
var t3 = AsyncWork3();
await Task.WhenAll(new[] { t1, t2, t3 });
//all task finished but now we have to execute the result behaviour in a sync way
Console.WriteLine($"Task1 finished with value {t1.Result}");
Console.WriteLine($"Task2 finished with value {t2.Result}");
Console.WriteLine($"Task3 finished with value {t3.Result}");
Console.ReadKey();
}//main
public static async Task<int> AsyncWork1() {
await Task.Delay(1000);
return 1;
}
public static async Task<string> AsyncWork2() {
await Task.Delay(100);
return "work2";
}
public static async Task<bool> AsyncWork3() {
await Task.Delay(500);
return true;
}
}//class Test
I am trying to return a bool true if the user selects yes from AlertDialog and visa versa.
at the moment it always returns false. it seems like the bool "result" is never being set.
public bool AskForConfirmation(string messege, Context context)
{
bool result;
Android.Support.V7.App.AlertDialog.Builder dialog = new Android.Support.V7.App.AlertDialog.Builder(context);
dialog.SetPositiveButton("Yes", (sender, args) =>
{
result = true;
});
dialog.SetNegativeButton("No", (sender, args) =>
{
result = false;
}).SetMessage(messege).SetTitle("System Message");
dialog.Show();
return result;
}
And I call the method
this.RunOnUiThread(() =>
{
bool response = ioManager.AskForConfirmation("Message", this);
Console.WriteLine("Response is " + response);
});
You can create a Task-based dialog via a ManualResetEvent or a TaskCompletionSource so you can call it like this:
Usage via TaskCompletionSource Example:
try
{
var result = await DialogAsync.Show(this, "StackOverflow", "Does it rock?");
Log.Debug("SO", $"Dialog result: {result}");
}
catch (TaskCanceledException ex)
{
Log.Debug("SO", $"Dialog cancelled; backbutton, click outside dialog, system-initiated, .... ");
}
DialogAsync via TaskCompletionSource Example:
public class DialogAsync : Java.Lang.Object, IDialogInterfaceOnClickListener, IDialogInterfaceOnCancelListener
{
readonly TaskCompletionSource<bool?> taskCompletionSource = new TaskCompletionSource<bool?>();
public DialogAsync(IntPtr handle, Android.Runtime.JniHandleOwnership transfer) : base(handle, transfer) { }
public DialogAsync() { }
public void OnClick(IDialogInterface dialog, int which)
{
switch (which)
{
case -1:
SetResult(true);
break;
default:
SetResult(false);
break;
}
}
public void OnCancel(IDialogInterface dialog)
{
taskCompletionSource.SetCanceled();
}
void SetResult(bool? selection)
{
taskCompletionSource.SetResult(selection);
}
public async static Task<bool?> Show(Activity context, string title, string message)
{
using (var listener = new DialogAsync())
using (var dialog = new AlertDialog.Builder(context)
.SetPositiveButton("Yes", listener)
.SetNegativeButton("No", listener)
.SetOnCancelListener(listener)
.SetTitle(title)
.SetMessage(message))
{
dialog.Show();
return await listener.taskCompletionSource.Task;
}
}
}
Usage Via ManualResetEvent Example:
using (var cancellationTokenSource = new CancellationTokenSource())
{
var result = await DialogAsync.Show(this, "StackOverflow", "Does it rock?", cancellationTokenSource);
if (!cancellationTokenSource.Token.IsCancellationRequested)
{
Log.Debug("SO", $"Dialog result: {result}");
}
else
{
Log.Debug("SO", $"Dialog cancelled; backbutton, click outside dialog, system-initiated, .... ");
}
}
DialogAsync via ManualResetEvent Example:
public class DialogAsync : Java.Lang.Object, IDialogInterfaceOnClickListener, IDialogInterfaceOnCancelListener
{
readonly ManualResetEvent resetEvent = new ManualResetEvent(false);
CancellationTokenSource cancellationTokenSource;
bool? result;
public DialogAsync(IntPtr handle, Android.Runtime.JniHandleOwnership transfer) : base(handle, transfer) { }
public DialogAsync() { }
public void OnClick(IDialogInterface dialog, int which)
{
switch (which)
{
case -1:
SetResult(true);
break;
default:
SetResult(false);
break;
}
}
public void OnCancel(IDialogInterface dialog)
{
cancellationTokenSource.Cancel();
SetResult(null);
}
void SetResult(bool? selection)
{
result = selection;
resetEvent.Set();
}
public async static Task<bool?> Show(Activity context, string title, string message, CancellationTokenSource source)
{
using (var listener = new DialogAsync())
using (var dialog = new AlertDialog.Builder(context)
.SetPositiveButton("Yes", listener)
.SetNegativeButton("No", listener)
.SetOnCancelListener(listener)
.SetTitle(title)
.SetMessage(message))
{
listener.cancellationTokenSource = source;
context.RunOnUiThread(() => { dialog.Show(); });
await Task.Run(() => { listener.resetEvent.WaitOne(); }, source.Token);
return listener.result;
}
}
}
I don't know how to run multi await methods in single method. For example my code as below:
public static async Task<bool> Authenticate()
{
bool authen = false;
string message = String.Empty;
try
{
session = await FacebookSessionClient.LoginAsync("user_about_me,read_stream");
fbAccessToken = session.AccessToken;
fbFacbookID = session.FacebookId;
await saveProfile(fbFacebookID); //error here,my app is closed at here
authen = true;
}
catch (InvalidOperationException e)
{
authen = false;
}
return authen;
}
And I have method save profile
public async static void saveProfile(string fbFacbookID)
{
string response = string.Empty;
if (!string.IsNullOrEmpty(fbFacbookID))
{
response=await StaticClass.getJsonStream(string.Format("http://graph.facebook.com/{0}", fbFacbookID));
JObject _object = JObject.Parse(response);
SaveValueSetting("usernameFB",(string)_object["username"]);
}
else
{
return;
}
}
But I cannot run method? So how do I fix it?
you can use below mentioned code.
private void Button_Click_1(object sender, RoutedEventArgs e)
{
MyMethod();
MyMethod1();
}
public async Task MyMethod()
{
Task<int> longRunningTask = LongRunningOperation();
//indeed you can do independent to the int result work here
//and now we call await on the task
int result = await longRunningTask;
//use the result
MessageBox.Show(result.ToString());
}
public async Task MyMethod1()
{
Task<int> longRunningTask = SecondMethod();
//indeed you can do independent to the int result work here
//and now we call await on the task
int result = await longRunningTask;
//use the result
MessageBox.Show(result.ToString());
}
public async Task<int> LongRunningOperation() // assume we return an int from this long running operation
{
await Task.Delay(5000); //5 seconds delay
return 1;
}
public async Task<int> SecondMethod()
{
await Task.Delay(2000);
return 1;
}
I am new to c#; I have mainly done Java.
I want to implement a timeout something along the lines:
int now= Time.now();
while(true)
{
tryMethod();
if(now > now+5000) throw new TimeoutException();
}
How can I implement this in C#? Thanks!
One possible way would be:
Stopwatch sw = new Stopwatch();
sw.Start();
while(true)
{
tryMethod();
if(sw.ElapsedMilliseconds > 5000) throw new TimeoutException();
}
However you currently have no way to break out of your loop. I would recommend having tryMethod return a bool and change it to:
Stopwatch sw = new Stopwatch();
sw.Start();
while(!tryMethod())
{
if(sw.ElapsedMilliseconds > 5000) throw new TimeoutException();
}
The question is quite old, but yet another option.
using(CancellationTokenSource cts = new CancellationTokenSource(5000))
{
cts.Token.Register(() => { throw new TimeoutException(); });
while(!cts.IsCancellationRequested)
{
tryMethod();
}
}
Technically, you should also propagate the CancellationToken in the tryMethod() to interupt it gracefully.
Working demo: (note I had to remove the exception throwing behavior as .netfiddle doesn't like it.)
https://dotnetfiddle.net/WjRxyk
I think you could do this with a timer and a delegate, my example code is below:
using System;
using System.Timers;
class Program
{
public delegate void tm();
static void Main(string[] args)
{
var t = new tm(tryMethod);
var timer = new Timer();
timer.Interval = 5000;
timer.Start();
timer.Elapsed += (sender, e) => timer_Elapsed(t);
t.BeginInvoke(null, null);
}
static void timer_Elapsed(tm p)
{
p.EndInvoke(null);
throw new TimeoutException();
}
static void tryMethod()
{
Console.WriteLine("FooBar");
}
}
You have tryMethod, you then create a delegate and point this delegate at tryMethod, then you start this delegate Asynchronously. Then you have a timer, with the Interval being 5000ms, you pass your delegate into your timer elapsed method (which should work as a delegate is a reference type, not an value type) and once the 5000 seconds has elapsed, you call the EndInvoke method on your delegate.
As long as tryMethod() doesn't block this should do what you want:
Not safe for daylight savings time or changing time zones when mobile:
DateTime startTime = DateTime.Now;
while(true)
{
tryMethod();
if(DateTime.Now.Subtract(startTime).TotalMilliseconds > 5000)
throw new TimeoutException();
}
Timezone and daylight savings time safe versions:
DateTime startTime = DateTime.UtcNow;
while(true)
{
tryMethod();
if(DateTime.UtcNow.Subtract(startTime).TotalMilliseconds > 5000)
throw new TimeoutException();
}
(.NET 3.5 or higher required for DateTimeOffset.)
DateTimeOffset startTime = DateTimeOffset.Now;
while(true)
{
tryMethod();
if(DateTimeOffset.Now.Subtract(startTime).TotalMilliseconds > 5000)
throw new TimeoutException();
}
Using Tasks for custom timeout on Async method
Here my implementation of a custom class with a method to wrap a task to have a timeout.
public class TaskWithTimeoutWrapper
{
protected volatile bool taskFinished = false;
public async Task<T> RunWithCustomTimeoutAsync<T>(int millisecondsToTimeout, Func<Task<T>> taskFunc, CancellationTokenSource cancellationTokenSource = null)
{
this.taskFinished = false;
var results = await Task.WhenAll<T>(new List<Task<T>>
{
this.RunTaskFuncWrappedAsync<T>(taskFunc),
this.DelayToTimeoutAsync<T>(millisecondsToTimeout, cancellationTokenSource)
});
return results[0];
}
public async Task RunWithCustomTimeoutAsync(int millisecondsToTimeout, Func<Task> taskFunc, CancellationTokenSource cancellationTokenSource = null)
{
this.taskFinished = false;
await Task.WhenAll(new List<Task>
{
this.RunTaskFuncWrappedAsync(taskFunc),
this.DelayToTimeoutAsync(millisecondsToTimeout, cancellationTokenSource)
});
}
protected async Task DelayToTimeoutAsync(int millisecondsToTimeout, CancellationTokenSource cancellationTokenSource)
{
await Task.Delay(millisecondsToTimeout);
this.ActionOnTimeout(cancellationTokenSource);
}
protected async Task<T> DelayToTimeoutAsync<T>(int millisecondsToTimeout, CancellationTokenSource cancellationTokenSource)
{
await this.DelayToTimeoutAsync(millisecondsToTimeout, cancellationTokenSource);
return default(T);
}
protected virtual void ActionOnTimeout(CancellationTokenSource cancellationTokenSource)
{
if (!this.taskFinished)
{
cancellationTokenSource?.Cancel();
throw new NoInternetException();
}
}
protected async Task RunTaskFuncWrappedAsync(Func<Task> taskFunc)
{
await taskFunc.Invoke();
this.taskFinished = true;
}
protected async Task<T> RunTaskFuncWrappedAsync<T>(Func<Task<T>> taskFunc)
{
var result = await taskFunc.Invoke();
this.taskFinished = true;
return result;
}
}
Then you can call it like this:
await new TaskWithTimeoutWrapper().RunWithCustomTimeoutAsync(10000, () => this.MyTask());
or
var myResult = await new TaskWithTimeoutWrapper().RunWithCustomTimeoutAsync(10000, () => this.MyTaskThatReturnsMyResult());
And you can add a cancellation token if you want to cancel the running async task if it gets to timeout.
Hope it helps
Another way I like to do it:
public class TimeoutAction
{
private Thread ActionThread { get; set; }
private Thread TimeoutThread { get; set; }
private AutoResetEvent ThreadSynchronizer { get; set; }
private bool _success;
private bool _timout;
/// <summary>
///
/// </summary>
/// <param name="waitLimit">in ms</param>
/// <param name="action">delegate action</param>
public TimeoutAction(int waitLimit, Action action)
{
ThreadSynchronizer = new AutoResetEvent(false);
ActionThread = new Thread(new ThreadStart(delegate
{
action.Invoke();
if (_timout) return;
_timout = true;
_success = true;
ThreadSynchronizer.Set();
}));
TimeoutThread = new Thread(new ThreadStart(delegate
{
Thread.Sleep(waitLimit);
if (_success) return;
_timout = true;
_success = false;
ThreadSynchronizer.Set();
}));
}
/// <summary>
/// If the action takes longer than the wait limit, this will throw a TimeoutException
/// </summary>
public void Start()
{
ActionThread.Start();
TimeoutThread.Start();
ThreadSynchronizer.WaitOne();
if (!_success)
{
throw new TimeoutException();
}
ThreadSynchronizer.Close();
}
}
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(10000);
try
{
Task task = Task.Run(() => { methodToTimeoutAfter10Seconds(); }, cts.Token);
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
using (cts.Token.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
{
if (task != await Task.WhenAny(task, tcs.Task))
{
throw new OperationCanceledException(cts.Token);
}
}
/* Wait until the task is finish or timeout. */
task.Wait();
/* Rest of the code goes here */
}
catch (TaskCanceledException)
{
Console.WriteLine("Timeout");
}
catch (OperationCanceledException)
{
Console.WriteLine("Timeout");
}
catch (Exception ex)
{
Console.WriteLine("Other exceptions");
}
finally
{
cts.Dispose();
}
Using mature library Polly it can be implemented using optimistic (thus CancellationToken based) as follows:
AsyncTimeoutPolicy policy = Policy.TimeoutAsync(60, TimeoutStrategy.Optimistic);
await policy.ExecuteAsync(async cancel => await myTask(cancel), CancellationToken.None);
myTask(cancel) should be of signature Func<CancellationToken, Task> e.g. async Task MyTast(CancellationToken token) {...}