Tasks and how to use them properly C# - c#

I am currently using tasks in some projects, and I was wondering how can I actually run a parallel code multiple times with the same method.
Here is my code:
Task CPU1 = new Task(CPU1_DoWork);
Task CPU2 = new Task(CPU2_DoWork);
// And i wanted to execute the CPU1 Task multiple time here is how i used it:
while (Timer_General.IsRunning == true)
{
if (CPU1.Status != TaskStatus.Running)
{
CPU1.Start();
}
if (CPU2.Status != TaskStatus.Running)
{
CPU2.Start();
}
}
// Other Blabla
Why is he running CPU1_DoWork only once? Thanks for your help guys! :)
I added the CPU2 sequence because if we await the first, the second will not get called :/ So this poses a problem. Is there any other way to make my Tasks run multiple time at the same time?

A Task cannot be re-used. For each one you want to use you'll need to create a new instance. Instead of using the Task, you should use the built in factory method Task.Run which will return a hot task for you:
public async Task FooAsync()
{
while (Timer_General.IsRunning)
{
await Task.Run(CPU1_DoWork);
}
}
As you're checking for Task.IsRunning, I'm assuming you want to asynchronously wait for each task to complete, hence I added the await keyword.
Edit:
Since you added a second task and want them botg to run in parallel, you can use Task.WhenAll:
public async Task FooAsync()
{
while (Timer_General.IsRunning)
{
await Task.WhenAll(Task.Run(CPU1_DoWork),
Task.Run(CPU2_Work));
}
}

Related

Nested async methods in a Parallel.ForEach

I have a method that runs multiple async methods within it. I have to iterate over a list of devices, and pass the device to this method. I am noticing that this is taking a long time to complete so I am thinking of using Parallel.ForEach so it can run this process against multiple devices at the same time.
Let's say this is my method.
public async Task ProcessDevice(Device device) {
var dev = await _deviceService.LookupDeviceIndbAsNoTracking(device);
var result = await DoSomething(dev);
await DoSomething2(dev);
}
Then DoSomething2 also calls an async method.
public async Task DoSomething2(Device dev) {
foreach(var obj in dev.Objects) {
await DoSomething3(obj);
}
}
The list of devices continuously gets larger over time, so the more this list grows, the longer it takes the program to finish running ProcessDevice() against each device. I would like to process more than one device at a time. So I have been looking into using Parallel.ForEach.
Parallel.ForEach(devices, async device => {
try {
await ProcessDevice(device);
} catch (Exception ex) {
throw ex;
}
})
It appears that the program is finishing before the device is fully processed. I have also tried creating a list of tasks, and then foreach device, add a new task running ProcessDevice to that list and then awaiting Task.WhenAll(listOfTasks);
var listOfTasks = new List<Task>();
foreach(var device in devices) {
var task = Task.Run(async () => await ProcessDevice(device));
listOfTasks.Add(task);
}
await Task.WhenAll(listOfTasks);
But it appears that the task is marked as completed before ProcessDevice() is actually finished running.
Please excuse my ignorance on this issue as I am new to parallel processing and not sure what is going on. What is happening to cause this behavior and is there any documentation that you could provide that could help me better understand what to do?
You can't mix async with Parallel.ForEach. Since your underlying operation is asynchronous, you'd want to use asynchronous concurrency, not parallelism. Asynchronous concurrency is most easily expressed with WhenAll:
var listOfTasks = devices.Select(ProcessDevice).ToList();
await Task.WhenAll(listOfTasks);
In your last example there's a few problems:
var listOfTasks = new List<Task>();
foreach (var device in devices)
{
await Task.Run(async () => await ProcessDevice(device));
}
await Task.WhenAll(listOfTasks);
Doing await Task.Run(async () => await ProcessDevice(device)); means you are not moving to the next iteration of the foreach loop until the previous one is done. Essentially, you're still doing them one at a time.
Additionally, you aren't adding any tasks to listOfTasks so it remains empty and therefore Task.WhenAll(listOfTasks) completes instantly because there's no tasks to await.
Try this:
var listOfTasks = new List<Task>();
foreach (var device in devices)
{
var task = Task.Run(async () => await ProcessDevice(device))
listOfTasks.Add(task);
}
await Task.WhenAll(listOfTasks);
I can explain the problem with Parallel.ForEach. An important thing to understand is that when the await keyword acts on an incomplete Task, it returns. It will return its own incomplete Task if the method signature allows (if it's not void). Then it is up to the caller to use that Task object to wait for the job to finish.
But the second parameter in Parallel.ForEach is an Action<T>, which is a void method, which means no Task can be returned, which means the caller (Parallel.ForEach in this case) has no way to wait until the job has finished.
So in your case, as soon as it hits await ProcessDevice(device), it returns and nothing waits for it to finish so it starts the next iteration. By the time Parallel.ForEach is finished, all it has done is started all the tasks, but not waited for them.
So don't use Parallel.ForEach with asynchronous code.
Stephen's answer is more appropriate. You can also use WSC's answer, but that can be dangerous with larger lists. Creating hundreds or thousands of new threads all at once will not help your performance.
not very sure it this if what you are asking for, but I can give example of how we start async process
private readonly Func<Worker> _worker;
private void StartWorkers(IEnumerable<Props> props){
Parallel.ForEach(props, timestamp => { _worker.Invoke().Consume(timestamp); });
}
Would recommend reading about Parallel.ForEach as it will do some part for you.

How to check task status in c#

I'd like to know how can i check status of an asynchronous task in c#.
I have a save method to save users, and i'd like to run a background task to update them after the save.
I'm in framework 4.0, here is my code to begin the task
System.Threading.Tasks.Task task = null;
task = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
beginTask();
});
My problem is the task take some times to end (near 7 mins) so if someone do several user saves, the task is runned several times, so i'd like to check before running the task if the function beginTask() is already running to avoid to have a lot of background tasks are running.
Thanks
Maybe try do it this way-
Initialise your task object:
Task task = new Task(begintask);
And add new method to run it, for example:
public void StartTask(Task t)
{
if (t.Status == TaskStatus.Running)
return;
else
t.Start();
}
Of course you can add more conditions depend of task's states.
I think I found better solution. Check if it suits you.
public class TaskDemo
{
private static AutoResetEvent autoReset = new AutoResetEvent(true);
Action beginTask = () =>
{
Console.WriteLine("Method start");
Thread.Sleep(2000);
};
public void RunTask()
{
Task myTask = Task.Run(() =>
{
autoReset.WaitOne();
beginTask();
}).ContinueWith(t => autoReset.Set());
}
}
And simply console app test:
static void Main(string[] args)
{
TaskDemo td = new TaskDemo();
// Simulation multiple requests
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
}
The clue is to use AutoResetEvent to signal task state and
Task myTask = Task.Run(() =>
{
autoReset.WaitOne();
beginTask();
}).ContinueWith(t => autoReset.Set());
to change its state during run and after finish (ContinueWith(t => autoReset.Set()).
Each Task object has a Task.Status property of type TaskStatus which can be queried if you already have the task object. It will tell you whether the task is running, finished, cancelled, etc.
If you want to check if ANY task is running that particular chunk of functionality that may be more difficult. One possible suggestion would be keeping a Task variable globally accessible for that reason and any time they tried to run that functionality the code did:
Check the global to see if one is running.
If not create a new Task to run it and assign it to the global variable
Else perform whatever handling you wanted to do if it was already running (wait for it to finish perhaps?)
It sounds like you're a) misunderstanding tasks a bit and b) need to take a look at your solution design.
A task is an object representing a piece of work to be done. A task is NOT the BeginTask method itself. The BeginTask method is more or less just a set of instructions to carry out. It doesn't have any state. Individual Tasks which implement those instructions do have a state which can be queried.
If you want to make it so only one Task could be run per user you'd just have to somewhere globally store a collection of Tasks per user (such as a Dictionary with the Key being the user).
This would ideally be created and stored in either some sort of governing class that contains this section of application functionality or in the outer program if it is one.
To make reference to your comment of "i need to avoid to create a new task everytime i save my users", for this you're going to have to adapt that particular piece of code to check the stored status of any running Tasks. So in my idea above you'd alter that piece of functionality to check if a Task exists for the user you're saving in the Dictionary of already begun tasks and if it does, check the status of it.
If you're not sure though please keep asking questions in the comments. Perhaps if you gave more information on how the system this is in, is structured I'd be better able to assist.
Hope that helps.
You can use property Task.IsCompleted or Task.Status
And by the way try to investigation of this method Task.ContinueWith
I think using of Task.ContinueWith is better way for multitask solutions.
See example below which explain my suggestion with using Task.ContinueWith:
System.Threading.Tasks.Task task = null;
if (task==null)
{
task = Task.Factory.StartNew(() =>
{
beginTask();
});
return;
}
if (task.Status == TaskStatus.Running)
{
task.ContinueWith((x) =>
{
beginTask();
});
}
else
{
task = Task.Factory.StartNew(() =>
{
beginTask();
});
}
This implementation can resolve scope of potential problem like:
How to keep previous Task
When should system launch previous Tasks

Task.WhenAll finishing before Tasks have completed

My code is continuing to execute before all tasks have been completed.
I've had a look at other people with a similar problem but can't see anything obvious!
static Task MoveAccountAsync(MoverParams moverParams)
{
return Task.Run(() =>
{
Console.WriteLine("Moving {0}", moverParams.Account.Name);
moverParams.Account.Mover.RefreshRoom();
moverParams.Account.Mover.PathfindTo(moverParams.Room);
});
}
static async void MoveAccountsAsync(List<Account> accounts, int room)
{
List<Task> theTasks = new List<Task>();
foreach (Account account in accounts)
{
// Create a new task and add it to the task list
theTasks.Add(MoveAccountAsync(new MoverParams(account, room)));
}
await Task.WhenAll(theTasks);
Console.WriteLine("Finished moving.");
}
Then simply calling it from static main:
MoveAccountsAsync(theAccounts, room);
Help much appreciated!
Cheers,
Dave
async void methods are highly discouraged and often times (e.g. here) sign of an issue.
Because you're not awaiting your method call (and you can't await it because it returns void) caller will not wait for all the work to finish before moving on to the next statement.
Change your method to return Task and await it to fix the problem. If you're calling into MoveAccountsAsync from synchronous context (e.g. Main method) use Wait to wait on the results. But be aware that in certain conditions (e.g. if run as part of ASP.NET application) that might cause deadlocks.

c# do the equivalent of restarting a Task with some parameter

The main idea here is to fetch some data from somewhere, when it's fetched start writing it, and then prepare the next batch of data to be written, while waiting for the previous write to be complete.
I know that a Task cannot be restarted or reused (nor should it be), although I am trying to find a way to do something like this :
//The "WriteTargetData" method should take the "data" variable
//created in the loop below as a parameter
//WriteData basically do a shedload of mongodb upserts in a separate thread,
//it takes approx. 20-30 secs to run
var task = new Task(() => WriteData(somedata));
//GetData also takes some time.
foreach (var data in queries.Select(GetData))
{
if (task.Status != TaskStatus.Running)
{
//start task with "data" as a parameter
//continue the loop to prepare the next batch of data to be written
}
else
{
//wait for task to be completed
//"restart" task
//continue the loop to prepare the next batch of data to be written
}
}
Any suggestion appreciated ! Thanks. I don't necessarily want to use Task, I just think it might be the way to go.
This may be over simplifying your requirements, but would simply "waiting" for the previous task to complete work for you? You can use Task.WaitAny and Task.WaitAll to wait for previous operations to complete.
pseudo code:
// Method that makes calls to fetch and write data.
public async Task DoStuff()
{
Task currTask = null;
object somedata = await FetchData();
while (somedata != null)
{
// Wait for previous task.
if (currTask != null)
Task.WaitAny(currTask);
currTask = WriteData(somedata);
somedata = await FetchData();
}
}
// Whatever method fetches data.
public Task<object> FetchData()
{
var data = new object();
return Task.FromResult(data);
}
// Whatever method writes data.
public Task WriteData(object somedata)
{
return Task.Factory.StartNew(() => { /* write data */});
}
The Task class is not designed to be restarted. so you Need to create a new task and run the body with the same Parameters. Next i do not see where you start the task with the WriteData function in its body. That will property Eliminate the call of if (task.Status != TaskStatus.Running) There are AFAIK only the class Task and Thread where task is only the abstraction of an action that will be scheduled with the TaskScheduler and executed in different threads ( when we talking about the Common task Scheduler, the one you get when you call TaskFactory.Scheduler ) and the Number of the Threads are equal to the number of Processor Cores.
To you Business App. Why do you wait for the execution of WriteData? Would it be not a lot more easy to gater all data and than submit them into one big Write?
something like ?
public void Do()
{
var task = StartTask(500);
var array = new[] {1000, 2000, 3000};
foreach (var data in array)
{
if (task.IsCompleted)
{
task = StartTask(data);
}
else
{
task.Wait();
task = StartTask(data);
}
}
}
private Task StartTask(int data)
{
var task = new Task(DoSmth, data);
task.Start();
return task;
}
private void DoSmth(object time)
{
Thread.Sleep((int) time);
}
You can use a thread and an AutoResetEvent. I have code like this for several different threads in my program:
These are variable declarations that belong to the main program.
public AutoResetEvent StartTask = new AutoResetEvent(false);
public bool IsStopping = false;
public Thread RepeatingTaskThread;
Somewhere in your initialization code:
RepeatingTaskThread = new Thread( new ThreadStart( RepeatingTaskProcessor ) ) { IsBackground = true; };
RepeatingTaskThread.Start();
Then the method that runs the repeating task would look something like this:
private void RepeatingTaskProcessor() {
// Keep looping until the program is going down.
while (!IsStopping) {
// Wait to receive notification that there's something to process.
StartTask.WaitOne();
// Exit if the program is stopping now.
if (IsStopping) return;
// Execute your task
PerformTask();
}
}
If there are several different tasks you want to run, you can add a variable that would indicate which one to process and modify the logic in PerformTask to pick which one to run.
I know that it doesn't use the Task class, but there's more than one way to skin a cat & this will work.

How do I create a Task that uses await inside the body that behaves the same as the synchronous version when Wait is called?

I have some code that creates a task that does some slow work like this:
public static Task wait1()
{
return new Task(() =>
{
Console.WriteLine("Waiting...");
Thread.Sleep(10000);
Console.WriteLine("Done!");
});
}
In the real implementation, the Thread.Sleep will actually be a web service call. I would like to change the body of the method can use await (so it does not consume a thread during the network access/sleep). My first attempt (based on shotgun-debugging the compile errors) was this:
public static Task wait2()
{
return new Task(async () =>
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
});
}
However; this task doesn't seem to behave the same as the first one, because when I call .Wait() on it; it returns immediately.
Below is a full sample (console app) showing the differences (the app will end immediately when the second task starts).
What do I need to do so that I can call Start and Wait on a Task which happens to have code using await inside it? The tasks are queued and executed later by an agent, so it's vital that the task is not auto-started.
class Program
{
static void Main(string[] args)
{
var w1 = wait1();
w1.Start();
w1.Wait(); // This waits 110 seconds
var w2 = wait2();
w2.Start();
w2.Wait(); // This returns immediately
}
public static Task wait1()
{
return new Task(() =>
{
Console.WriteLine("Waiting...");
Thread.Sleep(10000);
Console.WriteLine("Done!");
});
}
public static Task wait2()
{
return new Task(async () =>
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
});
}
}
It seems like this isn't possible! See alexm's answer here:
Tasks returned by async methods are always hot i.e. they are created in Running state.
:-(
I've worked around this by making my agent queue Func<Task>s instead, and the overload that receives a task simply queues () => task. Then; when de-queing a task, I check if it's not running, and if so, start it:
var currentTask = currentTaskFunction();
if (currentTask.Status == TaskStatus.Created)
currentTask.Start();
It seems a little clunky to have to do this (if this simple workaround works; why the original restriction on async methods always being created hot?), but it seems to work for me :-)
You could write this as:
public static async Task Wait2()
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
}
In general, it's rarely a good idea to ever use new Task or new Task<T>. If you must launch a task using the ThreadPool instead of using the async/await language support to compose one, you should use Task.Run to start the task. This will schedule the task to run (which is important, tasks should always be "hot" by conventions).
Note that doing this will make it so you don't have to call Task.Start, as well.
To help you understand this realize that async / await essentially does not create a new thread but rather it schedules that portion of code to be ran at an available point in time.
When you create the new Task(async () => ...) you have a task that run an async method. When that inner async method hits an await the 'new Task' is considered complete because the rest of it has been scheduled. To help you understand better place some code (a lot if wanted) in the 'new Task' before the await command. It will all execute before the application terminates and once await is reached that task will believe it has completed. It then returns and exits the application.
The best way to avoid this is to not place any task or async methods inside of your task.
Remove the async keyword and the await keyword from the method and it will work as expected.
This is the same as creating a callback if you're familiar with that.
void MethodAsync(Action callback)
{
//...some code
callback?.Invoke();
}
//using this looks like this.
MethodAsync(() => { /*code to run when complete */});
//This is the same as
Task MethodAsync()
{
//... some code here
}
//using it
await MethodAsync();
/*code to run when complete */
The thing to understand is that you're creating a new task within a task basically. So the inner 'callback' is being created at the await keyword.
You're code looks like this..
void MethodAsync(Action callback)
{
//some code to run
callback?.Invoke(); // <- this is the await keyword
//more code to run.. which happens after we run whoever is
//waiting on callback
}
There's code missing obviously. If this doesn't make sense please feel free to contact me and I'll assist. async / await (meant to make things simpler) is a beast to wrap your head around at first. Afterward you get it then it'll probably be your favorite thing in c# since linq. :P
Try this:
public async static Task wait2()
{
Console.WriteLine("Waiting...");
await Task.Delay(2000);
Console.WriteLine("Done!");
}
But we aware that the task is already started so you don't have to call start:
var w2 = wait2();
//w2.Start();
w2.Wait();
I think the problem with your wait2 function is that is creating 2 task, the one in new Task(...) and another in Task.Delay(). You are waiting for the first one, but you are not waiting for the inner one.

Categories

Resources