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.
Related
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%
}
I have an asynchronous method DoStuffAsync that spawns two tasks with Task.Run, and both tasks report their progress using a single IProgress<int> object. From the user's perspective there is only one operation, so showing two progress bars (one for each Task) wouldn't make any sense. This is why the IProgress<int> is shared. The problem is that sometimes the UI receives the progress notifications in incorrect order. Here is my code:
private async void Button1_Click(object sender, EventArgs e)
{
TextBox1.Clear();
var progress = new Progress<int>(x => TextBox1.AppendText($"Progress: {x}\r\n"));
await DoStuffAsync(progress);
}
async Task DoStuffAsync(IProgress<int> progress)
{
int totalPercentDone = 0;
Task[] tasks = Enumerable.Range(1, 2).Select(n => Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(100); // Simulate an I/O operation
var localPercentDone = Interlocked.Add(ref totalPercentDone, 10);
progress.Report(localPercentDone);
}
})).ToArray();
await Task.WhenAll(tasks);
}
Most of the time the notifications are in the correct order, but sometimes they are not:
This causes the ProgressBar control (not shown in the above screenshot) to jump awkwardly back and forth.
As a temporary solution I have added a lock inside the DoStuffAsync method, that includes the invocation of the IProgress.Report method:
async Task DoStuffAsync(IProgress<int> progress)
{
int totalPercentDone = 0;
object locker = new object();
Task[] tasks = Enumerable.Range(1, 2).Select(n => Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(100); // Simulate an I/O operation
lock (locker)
{
totalPercentDone += 10;
progress.Report(totalPercentDone);
};
}
})).ToArray();
await Task.WhenAll(tasks);
}
Although this solves the problem, it causes me anxiety because I invoke arbitrary code while holding a lock. The DoStuffAsync method is actually part of a library, and could be called with a whatever IProgress<int> implementation as argument. This opens the possibility for deadlock scenarios. Is there a better way to implement the DoStuffAsync method, without using a lock, but with the desired behavior regarding the ordering of the notifications?
Your problem is that you need the increment of totalPercentDone AND the call to Report to be atomic.
There's nothing wrong with using a lock here. After all, you need some way to make the two operations atomic. If you really don't want to use lock then you could use a SemaphoireSlim:
async Task DoStuffAsync(IProgress<int> progress)
{
int totalPercentDone = 0;
var semaphore = new SemaphoreSlim(1,1);
Task[] tasks = Enumerable.Range(1, 2).Select(n => Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(100); // Simulate an I/O operation
await semaphore.WaitAsync();
try
{
totalPercentDone += 10;
progress.Report(totalPercentDone);
}
finally
{
semaphore.Release();
}
}
})).ToArray();
await Task.WhenAll(tasks);
}
You could just report the deltas and let the handling handle them:
private async void Button1_Click(object sender, EventArgs e)
{
TextBox1.Clear();
var totalPercentDone = 0;
var progress = new Progress<int>(x =>
{
totalPercentDone += x;
TextBox1.AppendText($"Progress: {totalPercentDone}\r\n"));
}
await DoStuffAsync(progress);
}
async Task DoStuffAsync(IProgress<int> progress)
{
await Task.WhenAll(Enumerable.Range(1, 2).Select(n => Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(100); // Simulate an I/O operation
progress.Report(10);
}
})));
}
Instead of using one int for both tasks you could use two individual ints, and take the smallest of them. Each Task need to report to 100, and not 50.
async Task DoStuffAsync(IProgress<int> progress)
{
int[] totalPercentDone = new int[2];
Task[] tasks = Enumerable.Range(1, 2).Select(n => Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(100); // Simulate an I/O operation
totalPercentDone[n - 1] += 10;
progress.Report(totalPercentDone.Min());
}
})).ToArray();
await Task.WhenAll(tasks);
}
This is to extend my comments under question
Basically, progress is usually a forward-only value. With regards to reporting progress, it is likely that you never need to report a progress made in the past. Even you do, in most cases the client / event handler side would still drop such values received.
The problem here / why you need to synchronize reporting is mainly because you are reporting a progress of value type, whose value got copied when Report(T) is called.
You can simply avoid locking by reporting a reference type instance with the latest progress made:
public class DoStuffProgress
{
private volatile int _percentage;
public int Percentage => _percentage;
internal void IncrementBy(int increment)
{
Interlocked.Add(ref _percentage, increment);
}
}
Now your code looks like:
async Task DoStuffAsync(IProgress<DoStuffProgress> progress)
{
DoStuffProgress totalPercentDone = new DoStuffProgress();
Task[] tasks = Enumerable.Range(1, 2).Select(n => Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(100); // Simulate an I/O operation
totalPercentDone.IncrementBy(10);
// Report reference type object
progress.Report(totalPercentDone);
}
})).ToArray();
await Task.WhenAll(tasks);
}
The client, however, may receive notification with duplicate value:
Progress: 20
Progress: 20
Progress: 40
Progress: 40
Progress: 60
Progress: 60
Progress: 80
Progress: 80
Progress: 90
Progress: 100
But, the values should never be out of order.
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(...).
I have a console application with have two threads as:
public static async void Thread1()
{
for (int i = 0; i < 100; i++)
{
Debug.WriteLine("Thread1 " + i);
await MyFunc();
}
}
public static async void Thread2()
{
for (int i = 0; i < 100; i++)
{
Debug.WriteLine("Thread2 " + i);
await MyFunc();
}
}
public static void Main(string[] args)
{
MainAsync(args).GetAwaiter().GetResult();
}
private static async Task MainAsync(string[] args)
{
Console.WriteLine("Before start thread");
Thread tid1 = new Thread(Thread1);
Thread tid2 = new Thread(Thread2);
tid1.Start();
tid2.Start();
}
public static async Task MyFunc()
{
//do something
}
However, when the application run and terminates, it seems that only each thread is run just once as I see only below things in output:
Before start thread
Thread1 0
Thread2 0
//some thing from MyFunc
I expect or rather wannt to run each thread till the for loop.It seems to me that the for loop continues to run despite the await.
If yes, what could be other probable approach.
Any leads would be helpful.
You aren't doing anything to wait for the threads. The main routine will just continue on until it returns to the O/S, which will kill the process and any child threads. Since you aren't doing anything else, this happens almost immediately, cutting both threads' lives short.
If you want to wait for the threads to finish, you can refer to this answer and write some variation of
while (thread1.IsAlive || thread2.IsAlive)
{
//Do something to wait
}
...before exiting.
That being said, you should probably using Tasks instead of threads, e.g.
public static async Task Task1()
{
for (int i = 0; i < 100; i++)
{
Debug.WriteLine("Task1 " + i);
await MyFunc();
}
}
public static async Task Task2()
{
for (int i = 0; i < 100; i++)
{
Debug.WriteLine("Task2 " + i);
await MyFunc();
}
}
And then to execute and wait for both of them:
Task.WaitAll
(
new[]
{
Task1(),
Task2()
}
);
See this code in action on DotNetFiddle
See also What is the difference between tasks and threads?
You seem to have a lot of confusion about the role of threads and tasks, so it's a good idea to read up about it. Steven Cleary has a nice write-up about this. "There Is No Thread"
From the comments, it seems that your actual intention here is to run two async tasks in parallel, then to wait until they are both finished.
If you want to wait for two async tasks to complete in parallel, make sure your async methods actually return Task then:
Task task1 = DoSomethingAsync(); //don't await
Task task2 = DoSomethingElseAsync(); //don't await
then you can wait asynchronously for Task.WhenAll:
await Task.WhenAll(task1,task2);
You really don't need to be involving Thread at all.
Use async Task instead of async void
private static async Task MainAsync(string[] args)
{
Console.WriteLine("Before start thread");
var task1 = Thread1();
var task2 = Thread2();
var taskList = new [] { task1, task2 };
Task.WaitAll(taskList);
}
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;
}