Task.Delay vs Thread.Sleep difference - c#

private static async Task MainFunc()
{
var watch = System.Diagnostics.Stopwatch.StartNew();
List<Task<int>> list = new List<Task<int>>();
for (int i = 1; i <= 3; i++)
{
list.Add(TaskFunc(i));
}
var taskResult = await Task.WhenAll(list);
foreach (var item in taskResult)
{
Console.Write($"i= {item}.{ Environment.NewLine }");
}
list.Clear();
watch.Stop();
var elapsedMs1 = watch.ElapsedMilliseconds;
Console.WriteLine($"Total execution time: { elapsedMs1 }");
Console.WriteLine();
watch.Restart();
for (int i = 1; i <= 3; i++)
{
list.Add(Task.Run(() => ThreadFunc(i)));
}
var threadResult = await Task.WhenAll(list);
foreach (var item in threadResult)
{
Console.Write($"i= {item}.{ Environment.NewLine }");
}
watch.Stop();
var elapsedMs2 = watch.ElapsedMilliseconds;
Console.WriteLine($"Total execution time: { elapsedMs2 }");
}
private static async Task<int> TaskFunc(int i)
{
if (i == 1)
await Task.Delay(2000);
else if (i == 2)
await Task.Delay(1000);
else if (i == 3)
await Task.Delay(5000);
return i;
}
private static int ThreadFunc(int i)
{
if (i == 1)
Thread.Sleep(2000);
else if (i == 2)
Thread.Sleep(1000);
else if (i == 3)
Thread.Sleep(5000);
return i;
}
In this example there are two functions, TaskFunc and ThreadFunc, which are called from MainFunc seperately. I am curious as to why the second method doesn't seem to have any effect, and seems to be skipped. Not even the Thread.Sleep(...) seems to get executed.
As you can see, the total execution time for ThreadFunc is very short, even though the sleep timers should be same for both methods. Also, i is always set to 4. I suppose that the main thread is doing something wrong. Can someone explain what is going on here?

The issue here is scoping. The i you are using inside the Task.Run(() => TheadFunc(i)) is not a new integer, but rather, since it's a delegate, the value of i will only be retrieved once the delegate is executed.
This leads to i being 4 in all cases, since your for-loop increases it that much. But since you do not have an if-condition for a value of 4, it won't perform any delays with Thread.Sleep(...).

Related

Good way to make Userdialog.Instance.Progress wait for method to execute C# Xamarin

I'm trying to show a progress dialog showing the percent done with the method to wait everything + an await Task.Delay(20); And an await for the method I want to execute. Now I notice that with that task.delay the execution takes much longer.
What I want to achieve instead is that the progress.dialog calculates how long the method takes instead of putting a delay on it because this works a bit more slowly.
What are my options in this?
This is my code
private async Task DownloadAllAlert()
{
//alert to download everything
bool result = await DisplayAlert("Download", "Do you want to download everything?", "Yes", "No"); ;
//alert is user chose yes
if (result)
{
// loading dialog in percentage till downloading is done
using (var progress = UserDialogs.Instance.Progress("Loading..."))
{
for (var i = 0; i < 100; i++)
{
progress.PercentComplete = i;
await Api.DownloadAll();
await Task.Delay(20);
}
}
}
}
try using Task in c#.
using (var progress = UserDialogs.Instance.Progress("Loading..."))
{
await LoadData(ref progress);
}
call this function
public async Task LoadData(ref IProgressDialog progress);
{
//await Task.Yield(); //add this line of code if on ios didnt work
Task task1 = new Task(() =>
{
for (int i = 0; i < 50 ; i++) // loop untill 50% then increase sleeping thread time
{
progress.PercentComplete = i;
//sleeping for 1 second
Thread.Sleep(1000);
}
for (int i = 50; i < 100; i++) // loop untill 99% then stop
{
progress.PercentComplete = i;
//sleeping for 2 second
Thread.Sleep(2000);
}
Console.WriteLine("Task 1 complete");
});
Task task2 = new Task(() =>
{
await Api.DownloadAll();
Console.WriteLine("Task 2 complete");
});
//starting the tasks
task1.Start();
task2.Start();
Task.WaitAny(task2);
progress.PercentComplete = 100; // when task2 finish put it 100%
}

How to launch N tasks (or threads) to run the same method that may return useless results?

I have a few stochastic functions that returns a Maybe<T>. When it produces a useful result, Maybe contains the result.
Maybe<T> is implemented like this:
public readonly struct Maybe<T> {
public readonly bool ContainsValue;
public readonly T Value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Maybe(bool containsValue, T value) {
ContainsValue = containsValue;
Value = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Maybe<T> Just(T value) {
return new Maybe<T>(
containsValue: true,
value: value);
}
public static Maybe<T> Empty { get; } = new Maybe<T>(
containsValue: false,
value: default
);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Maybe<T>(T value) => Maybe.Just(value);
}
I'd like to spawn create N tasks to run FuncThatMayFail(), with N = Environment.ProcessorCount. When the first task / thread actually get a useful result, it stops and tells the other tasks / threads to stop too.
My current approach is this:
public static Maybe<T> RunParallel<T>(int maximumRetries, Func<Maybe<T>> func) {
if (maximumRetries < 0)
throw new ArgumentOutOfRangeException(nameof(maximumRetries) + " must be >= 0");
if (func == null)
throw new ArgumentNullException(nameof(func));
var retries = 0;
var tasks = new Task<Maybe<T>>[Environment.ProcessorCount];
var finished = 0;
for (int i = 0; i < tasks.Length; i++) {
tasks[i] = Task.Run(() => {
while (true) {
if (retries >= maximumRetries || finished > 0)
return Maybe<T>.Empty;
var attempt = func();
if (attempt.ContainsValue) {
Interlocked.Increment(ref finished);
return attempt;
} else {
Interlocked.Increment(ref retries);
}
}
});
}
Task.WaitAny(tasks);
for (int i = 0; i < tasks.Length; i++) {
var t = tasks[i];
if (t.IsCompletedSuccessfully && t.Result.ContainsValue)
return t.Result;
}
return Maybe<T>.Empty;
}
I posted this on codereview asking for improvement suggestions and got none.
I feel this code is ugly and that there probably is a better way to do this.
Is there a more elegant (without using external libraries) to achieve this?
I'm using C# 7.2 targeting .Net Core 2.2
I've updated the code and posted it below. It's not tested but the answer is in there. You should be able to run as is but if not take what you need out of it.
First you need to add a CancellationTokenSource and pass the Token to the Task(s)
started so that you can signal them when to stop (from the frameworks
perspective).
Then you need to monitor that CancellationTokenSource yourself in the while loop to manually stop the tasks.
Task.WaitAny returns the index of the Task that was
completed so you don't need to iterate through them to find it.
You're also already returning Maybe<T>.Empty if the Task
ends without a result so no need to test ContainsValue; just
return the Result.
Code is below and documented where I made changes.
//Make a cancellation token source to signal other tasks to cancel.
CancellationTokenSource cts = new CancellationTokenSource();
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = Task.Run(() => {
while (!cts.IsCancellationRequested) //Monitor for the cancellation token source to signal canceled.
{
if (retries >= maximumRetries || finished > 0)
return Maybe<T>.Empty;
var attempt = func();
if (attempt.ContainsValue)
{
Interlocked.Increment(ref finished);
return attempt;
}
else
{
Interlocked.Increment(ref retries);
}
}
return Maybe<T>.Empty;
}, cts.Token); //Add the token to the task.
}
var completedTaskIndex = Task.WaitAny(tasks); //Task.WaitAny gives you the index of the Task that did complete.
cts.Cancel(); //Signal the remaining tasks to complete.
var completedTask = tasks[completedTaskIndex]; //Get the task that completed.
return completedTask.Result; //You're returning Maybe<T>.Emtpy from the Task if it fails so no need to check ContainsValue; just return the result.

initialize task and start it later [duplicate]

This question already has answers here:
How to delay 'hot' tasks so they can processed in a set order
(2 answers)
Closed 4 years ago.
I need to first create new task then do some remaining work and then start the task that works with its result.
Simplified example:
static int value;
static async Task work1()
{
do
{
int i;
for (i = 0; i < 10000000; i++) {} // some calculations
Console.WriteLine("result1: " + value + " i: " + i);
await Task.Delay(2000).ConfigureAwait(false);
} while (condition);
}
static async Task work2()
{
do
{
int i;
for (i = 0; i < 10000000; i++) {} // some calculations
Console.WriteLine("result2: " + value + " i: " + i);
await Task.Delay(2000).ConfigureAwait(false);
} while (condition);
}
static void Main(string[] args)
{
Task task;
int tempvalue = 100;
if (condition1)
{
tempvalue *= 10;
task = new Task(() => work1());
} else
{
tempvalue -= 5;
task = new Task(() => work2());
}
if (tempvalue > 100)
{
value = 5;
} else
{
value = tempvalue;
}
task.Start();
// immediately do remaining work
}
this code does exactly what I need but compiler shows following warning:
Warning CS4014 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
on line:
Task task = new Task(() => work());
should I rather use it like so? Is there any difference?
Task task = new Task(async () => await work());
This is not a duplicate of How to delay 'hot' tasks so they can processed in a set order because after task.Start(); it should do remaining work immediately.
Func<Task> f = () => work();
// do stuff
f(); // blocks thread until work1() or work2() hits await
// do remaining work
The async keyword means that the task within your task is asynchronous and using await will mean that you want to wait for the method work to finish.
You could also use Task.Wait() in order to wait for the method to finish it's execution.
But using async await is the better way to do it because it's not blocking the main thread.

how to continue completed tasks?

public int CalcGroup(CancellationTokenSource cts)
{
try
{
CancellationToken ct = cts.Token;
if (cts.IsCancellationRequested == true && TaskWorkStatus.IsContinue == false) return 0;
for (int i = 0; i < _paralellTaskCount; i++)
{
int counter = CheckCounter(message);
if (counter >= 0)
{
var myTask = new Task<long>(() => CalcSingle(_personnelIds[counter].Item1), ct, TaskCreationOptions.LongRunning);
if(myTask.IsCompleted)
myTask.ContinueWith(t => CalcSingle(_personnelIds[counter].Item1), ct);
else
myTask.Start();
}
}
}
catch (Exception)
{
return 0;
}
return 1;
}
In above code block I want to run tasks that are Canceled and Completed, but it doesn't work.
What is my mistake?
0) Creating a new Task via the constructor is a bad practice since running it will later incur extra overhead(you can google the why's and how's later). Use Task.Run or Task.Factory.StartNew, depending on your .NET version.. (I've recently learned that Task.Run is a better practice for .NET 4.5 and later).
1) Creating a task does not start it, so the current logic will always skip the first condition, start the task and no continuation will ever occur.
2) even if you start the task, there is no guarantee that the task will finish in time for the condition checking.. this is why Task.ContinueWith has an overload which accepts a TaskContinuationOptions enumeration.. you have OnlyOnRanToCompletion, OnlyOnFaulted, etc..
3) To sum it up, here is your code, after a little fine tuning
for (int i = 0; i < _paralellTaskCount; i++)
{
object message;
int counter = CheckCounter(message);
if (counter >= 0)
{
var task = Task.Run(() => CalcSingle(_personnelIds[counter].Item1));
var continuation = task.ContinueWith((antecedent) => CalcSingle(_personnelIds[counter].Item1),
TaskContinuationOptions.OnlyOnRanToCompletion);
}
}
(Note that the continuation receives the antecedent task, which is the task it continues..
4) You do not seem to check if an exception has occured in any task, in any of the many ways provided.. note that wrapping the statement in a try catch clause will not transfer the exception to your thread.. (You need to access the Result or Exception properties, invoke Wait() or use the await keyword in order to accomplish that.
5) Here is an async implementation which does the same, and checks for exceptions
for (int i = 0; i < _paralellTaskCount; i++)
{
try
{
object message;
int counter = CheckCounter(message);
if (counter >= 0)
{
long res1 = await Task.Run(() => CalcSingle(_personnelIds[counter].Item1));
long res2 = await Task.Run(() => CalcSingle(_personnelIds[counter].Item1));
}
}
catch (AggregateException e)
{
//TODO handle
}
}
6) Please do read some articles which regard TPL, as it seems that you missed a few very important notions.

Restart concurrent tasks as soon as they fail for x number of times

I have a console app that is making HTTP queries and adding/updating products in my database according to response. Some fail and need to be retried a few times.
The way I came up with was to use a dictionary to store the product ID and a Task. Then I can check all the task results and re-run.
This is working but it strikes me as inefficient. Tasks are not being re-created until all tasks have finished. It would be more efficient if they were immediately restarted but I can't figure out how to do this. Also every retry involves a query to the database as only the ID is stored.
I made small app that shows how I am currently retrying failed requests.
Can someone suggest a more efficient method for retrying?
class Program
{
private static void Main(string[] args)
{
HttpQuery m = new HttpQuery();
var task = Task.Run(() => m.Start());
Task.WaitAll(task);
Console.WriteLine("Finished");
Console.ReadLine();
}
}
class HttpQuery
{
public async Task Start()
{
// dictionary where key represent reference to something that needs to be processed and bool whether it has completed or not
ConcurrentDictionary<int, Task<bool>> monitor = new ConcurrentDictionary<int, Task<bool>>();
// start async tasks.
Console.WriteLine("starting first try");
for (int i = 0; i < 1000; i++)
{
Console.Write(i+",");
monitor[i] = this.Query(i);
}
// wait for completion
await Task.WhenAll(monitor.Values.ToArray());
Console.WriteLine();
// start retries
// number of retries per query
int retries = 10;
int count = 0;
// check if max retries exceeded or all completed
while (count < retries && monitor.Any(x => x.Value.Result == false))
{
// make list of numbers that failed
List<int> retryList = monitor.Where(x => x.Value.Result == false).Select(x => x.Key).ToList();
Console.WriteLine("starting try number: " + (count+1) + ", Processing: " + retryList.Count);
// create list of tasks to wait for
List<Task<bool>> toWait = new List<Task<bool>>();
foreach (var i in retryList)
{
Console.Write(i + ",");
monitor[i] = this.Query(i);
toWait.Add(monitor[i]);
}
// wait for completion
await Task.WhenAll(toWait.ToArray());
Console.WriteLine();
count++;
}
Console.WriteLine("ended");
Console.ReadLine();
}
public async Task<bool> Query(int i)
{
// simulate a http request that may or may not fail
Random r = new Random();
int delay = i * r.Next(1, 10);
await Task.Delay(delay);
if (r.Next(0,2) == 1)
{
return true;
}
else
{
return false;
}
}
}
You can create another method and wrap all these ugly retry logic. All of that ugly code goes away :)
public async Task Start()
{
const int MaxNumberOfTries = 10;
List<Task<bool>> tasks = new List<Task<bool>>();
for (int i = 0; i < 1000; i++)
{
tasks.Add(this.QueryWithRetry(i, MaxNumberOfTries));
}
await Task.WhenAll(tasks);
}
public async Task<bool> QueryWithRetry(int i, int numOfTries)
{
int tries = 0;
bool result;
do
{
result = await Query(i);
tries++;
} while (!result && tries < numOfTries);
return result;
}

Categories