I am trying to run a task List using the following method:
List<Task> tasks = new List<Task>();
tasks.Add(new Task(() => this.firstMethod()));
tasks.Add(new Task(() => this.secondMethod()));
However, if I use one of the two examples below I get the following issues:
foreach (Task task in tasks)
{
await Task.Run(() => task);
}
In this first case, the tasks don't run at all.
foreach (Task task in tasks)
{
task.Start();
task.Wait();
}
In this second case, it runs only once, then I get the following error:
Start may not be called on a task that has completed
What am I missing?
You cannot re-use a Task. So let's start with creating an array of delegates
List<Action> tasks = new List<Action>();
tasks.Add(this.firstMethod);
tasks.Add(this.secondMethod);
and then run them sequentially, on additional threads (leaving the main thread free to update the UI):
foreach (Action task in tasks)
{
await Task.Run(task);
}
but this loop could be done in many ways, depending on the context of the calling code and the nature of the payloads.
This may be not the best solution.
Here's a unit test that shows how you can do this:
public class TasksTests
{
private readonly ITestOutputHelper _output;
public TasksTests(ITestOutputHelper output)
{
_output = output ?? throw new ArgumentNullException(nameof(output));
}
[Fact]
public async Task CanCreateAndRunTasks()
{
var tasks = new List<Task>
{
new Task(() => _output.WriteLine("Task #1")),
new Task(() => _output.WriteLine("Task #2"))
};
tasks.ForEach(t => t.Start());
await Task.WhenAll(tasks);
}
}
You first create the tasks. Then you need to start them. Lastly, you need to await them all, e.g., using Task.WhenAll.
the first case sample
foreach (Task task in tasks)
{
await Task.Run(() => task);
}
should be changed to
foreach (Task task in tasks)
{
task.Start(); // there is no sense to await since tasks should be run in parallel I suppose
}
The second case is not clear however.
Do you run the second case after the first one ? It should be fine if you have tasks just initialized before the Start call, like
List<Task> tasks = new List<Task>()
{
new Task(() => this.firstMethod()),
new Task(() => this.secondMethod()),
};
foreach (Task task in tasks)
{
task.Start();
task.Wait(); // this causes tasks to be run in sequence (one by one) like with await in the first sample
}
the most simplest case is to use Task.Run
var tasks = new[Task]
{
Task.Run(firstMethod),
Task.Run(secondMethod),
}; // tasks are started immediately there is no need to start them later
Task.WaitAll(tasks); // wait until all tasks are finished
Related
I have two services that ultimately both update the same object, so we have a test to ensure that the writes to that object complete (Under the hood we have retry policies on each).
9 times out of 10, one or more of the theories will fail, with the task.ShouldNotBeNull(); always being the assertion to fail. What am i getting wrong with the async code in this sample? Why would the task be null?
[Theory]
[InlineData(1)]
[InlineData(5)]
[InlineData(10)]
[InlineData(20)]
public async Task ConcurrencyIssueTest(int iterations)
{
var orderResult = await _driver.PlaceOrder();
var tasksA = new List<Task<ApiResponse<string>>>();
var tasksB = new List<Task<ApiResponse<string>>>();
await Task.Run(() => Parallel.For(1, iterations,
x =>
{
tasksA.Add(_Api.TaskA(orderResult.OrderId));
tasksB.Add(_Api.TaskB(orderResult.OrderId));
}));
//Check all tasks return successful
foreach (var task in tasksA)
{
task.ShouldNotBeNull();
var result = task.GetAwaiter().GetResult();
result.ShouldNotBeNull();
result.StatusCode.ShouldBe(HttpStatusCode.OK);
}
foreach (var task in tasksB)
{
task.ShouldNotBeNull();
var result = task.GetAwaiter().GetResult();
result.ShouldNotBeNull();
result.StatusCode.ShouldBe(HttpStatusCode.OK);
}
}
}
There's no need for Tasks and Parrallel looping here. I'm presuming that your _api calls are IO bound? You want something more like this:
var tasksA = new List<Task<ApiResponse<string>>>();
var tasksB = new List<Task<ApiResponse<string>>>();
//fire off all the async tasks
foreach(var it in iterations){
tasksA.Add(_Api.TaskA(orderResult.OrderId));
tasksB.Add(_Api.TaskB(orderResult.OrderId));
}
//await the results
await Task.WhenAll(tasksA).ConfigureAwait(false);
foreach (var task in tasksA)
{
//no need to get GetAwaiter(), you've awaited above.
task.Result;
}
//to get the most out of the async only await them just before you need them
await Task.WhenAll(tasksB).ConfigureAwait(false);
foreach (var task2 in tasksB)
{
task2.Result;
}
this will fire all your api calls async then block while the results return. You Parallel for and tasks are just using additional thread pool threads to zero benefit.
If _api is CPU bound you could get benefit from Task.Run but I'm guessing these are web api or something. So the Task.Run is doing nothing but using an additional thread.
As others have suggested, remove the Parallel, and await on all tasks to finish before asserting them.
I would also recommend to remove .Result from each task, and await them instead.
public async Task ConcurrencyIssueTest(int iterations)
{
var orderResult = await _driver.PlaceOrder();
var taskA = _Api.TaskA(orderResult.OrderId);
var taskB = _Api.TaskB(orderResult.OrderId);
await Task.WhenAll(taskA, taskB);
var taskAResult = await taskA;
taskAResult.ShouldNotBeNull();
taskAResult.StatusCode.ShouldBe(HttpStatusCode.OK);
var taskBResult = await taskB;
taskBResult.ShouldNotBeNull();
taskBResult.StatusCode.ShouldBe(HttpStatusCode.OK);
}
I'm trying to use Task.WaitAll on a list of tasks. The thing is the tasks are an async lambda which breaks Tasks.WaitAll as it never waits.
Here is an example code block:
List<Task> tasks = new List<Task>();
tasks.Add(Task.Factory.StartNew(async () =>
{
using (dbContext = new DatabaseContext())
{
var records = await dbContext.Where(r => r.Id = 100).ToListAsync();
//do long cpu process here...
}
}
Task.WaitAll(tasks);
//do more stuff here
This doesn't wait because of the async lambda. So how am I supposed to await I/O operations in my lambda?
Task.Factory.StartNew doesn't recognise async delegates as there is no overload that accepts a function returning a Task.
This plus other reasons (see StartNew is dangerous) is why you should be using Task.Run here:
tasks.Add(Task.Run(async () => ...
This doesn't wait because of the async lambda. So how am I supposed to
await I/O operations in my lambda?
The reason Task.WaitAll doesn't wait for the completion of the IO work presented by your async lambda is because Task.Factory.StartNew actually returns a Task<Task>. Since your list is a List<Task> (and Task<T> derives from Task), you wait on the outer task started by StartNew, while ignoring the inner one created by the async lambda. This is why they say Task.Factory.StartNew is dangerous with respect to async.
How could you fix this? You could explicitly call Task<Task>.Unwrap() in order to get the inner task:
List<Task> tasks = new List<Task>();
tasks.Add(Task.Factory.StartNew(async () =>
{
using (dbContext = new DatabaseContext())
{
var records = await dbContext.Where(r => r.Id = 100).ToListAsync();
//do long cpu process here...
}
}).Unwrap());
Or like others said, you could call Task.Run instead:
tasks.Add(Task.Run(async () => /* lambda */);
Also, since you want to be doing things right, you'll want to use Task.WhenAll, why is asynchronously waitable, instead of Task.WaitAll which synchronously blocks:
await Task.WhenAll(tasks);
You can do like this.
void Something()
{
List<Task> tasks = new List<Task>();
tasks.Add(ReadAsync());
Task.WaitAll(tasks.ToArray());
}
async Task ReadAsync() {
using (dbContext = new DatabaseContext())
{
var records = await dbContext.Where(r => r.Id = 100).ToListAsync();
//do long cpu process here...
}
}
you have to use the Task.ContinueWith method. Like this
List<Task> tasks = new List<Task>();
tasks.Add(Task.Factory.StartNew(() =>
{
using (dbContext = new DatabaseContext())
{
return dbContext.Where(r => r.Id = 100).ToListAsync().ContinueWith(t =>
{
var records = t.Result;
// do long cpu process here...
});
}
}
}
WaitAll not working. I get Done before tasks are completed.
I have some groups. each group contains items i want each item to be processed in a separate task.
static void Main(string[] args)
{
List<Task> TaskList = new List<Task>();
foreach (string group in groups)
{
Task task = new Task(() => { scan_group(group, timeout); });
task.Start();
TaskList.Add(task);
}
Task.WaitAll(TaskList.ToArray());
Console.WriteLine("- Done !" );
}
public static void scan_group (){
Task.Factory.StartNew(() => Parallel.ForEach<string>(items, x =>
{
scan(x, y);
}));
}
All your scan_group does is start a task without waiting for it to complete, or returning it to be waited for outside.
So you only wait for the internal tasks to be created, you don't wait for them to complete. That's why you get Done before your internal tasks run.
If you want to wait for the scan_group tasks, return them and store them in the list instead of creating tasks using the task constructor. For example:
foreach (string group in groups)
{
TaskList.Add(Task.Factory.StartNew(() => Parallel.ForEach<string>(items, x => scan(x, y))));
}
Task.WaitAll(TaskList.ToArray());
Note: Using the Task constructor directly is almost never the right solution. Also Task.Run is preferable to Task.Factory.StartNew if you're on .Net 4.5 and above.
This is happening because you are not awaiting any tasks. The Task.WaitAll does wait till all your tasks of scan_group() are done. Your scan_group() method however should be an async task.
Inside this method you can then use await on any task you create there. At the moment you are only starting a new task in the scan_group() method but you do not await these tasks. This causes the tasks to be started somewhere on another thread but the thread starting it already moves on and ends the method. The calling method sees this as your task being done.
Some implementation:
Declaration of scan_group():
public static async Task scan_group()
{
// create tasks for your tasks inside this method.
List<Task> itemTasks = new List<Task>();
items.foreach(x => itemTasks.Add(scan(x,y));
await Task.WhenAll(itemTasks.ToArray());
}
Your call to scan_group would stay the same.
I am writing a set of async tasks that go away an download and parse data, however I am running in to a bit of a blank with the next step where I am updating a database.
The issue is that for the sake of performance I am using a TableLock to load rather large datasets, so what I am wanting to do is have my import service wait for the first Task to return, start the import. Should another Task complete while the first import is running the process joins a queue and waits for the import service is complete for task 1.
eg.
Async
- Task1
- Task2
- Task3
Sync
- ImportService
RunAsync Tasks
Task3 returns first > ImportService.Import(Task3)
Task1 return, ImportService is still running. Wait()
ImportService.Complete() event
Task2 returns. Wait()
ImportService.Import(Task1)
ImportService.Complete() event
ImportService.Import(Task2)
ImportService.Complete() event
Hope this makes sense!
You can't really use await here, but you can wait on multiple tasks to complete:
var tasks = new List<Task)();
// start the tasks however
tasks.Add(Task.Run(Task1Function);
tasks.Add(Task.Run(Task2Function);
tasks.Add(Task.Run(Task2Function);
while (tasks.Count > 0)
{
var i = Task.WaitAny(tasks.ToArray()); // yes this is ugly but an array is required
var task = tasks[i];
tasks.RemoveAt(i);
ImportService.Import(task); // do you need to pass the task or the task.Result
}
Seems to me however that there should be a better option. You could let the tasks and the import run and add a lock on the ImportService part for instance:
// This is the task code doing whatever
....
// Task finishes and calls ImportService.Import
lock(typeof(ImportService)) // actually the lock should probably be inside the Import method
{
ImportService.Import(....);
}
There are several things bothering me with your requirements (including using a static ImportService, static classes are rarely a good idea), but without further details I can't provide better advice.
While this is likely not the most graceful solution, I would try launching the work tasks and have them place their output in a ConcurrentQueue. You could check the queue for work on a timer until all tasks are completed.
var rand = new Random();
var importedData = new List<string>();
var results = new ConcurrentQueue<string>();
var tasks = new List<Task<string>>
{
new Task<string>(() =>
{
Thread.Sleep(rand.Next(1000, 5000));
Debug.WriteLine("Task 1 Completed");
return "ABC";
}),
new Task<string>(() =>
{
Thread.Sleep(rand.Next(1000, 5000));
Debug.WriteLine("Task 2 Completed");
return "FOO";
}),
new Task<string>(() =>
{
Thread.Sleep(rand.Next(1000, 5000));
Debug.WriteLine("Task 3 Completed");
return "BAR";
})
};
tasks.ForEach(t =>
{
t.ContinueWith(r => results.Enqueue(r.Result));
t.Start();
});
var allTasksCompleted = new AutoResetEvent(false);
new Timer(state =>
{
var timer = (Timer) state;
string item;
if (!results.TryDequeue(out item))
return;
importedData.Add(item);
Debug.WriteLine("Imported " + item);
if (importedData.Count == tasks.Count)
{
timer.Dispose();
Debug.WriteLine("Completed.");
allTasksCompleted.Set();
}
}).Change(1000, 100);
allTasksCompleted.WaitOne();
What is the correct syntax for parallelizing the following code?
static void Main(string[] args)
{
Task.Factory.StartNew(
() =>
doOne(SelectedTask.option1)
.ContinueWith(
task =>
doOne(SelectedTask.option1)).Wait()
);
}
Same method with enum "selectedTask" to decide which code to execute :
static enum SelectedTask
{
option1,
option2
}
static void doOne(SelectedTask Lunch)
{
switch (lunch)
{
case SelectedTask.option1:
Console.WriteLine("option1");
break;
case SelectedTask.option2:
Console.WriteLine("option2");
break;
default:
break;
}
}
Do you want your doOne calls to occur concurrently? Then you can just start them straight from the task factory:
// Start two concurrent tasks
var task1 = Task.Factory.StartNew(() => doOne(SelectedTask.option1));
var task2 = Task.Factory.StartNew(() => doOne(SelectedTask.option2));
// Block the current thread until all tasks complete
Task.WaitAll(task1, task2);
Do you want your doOne calls to occur sequentially? Then you can chain them using ContinueWith:
// Start a chain of tasks
var task1 = Task.Factory.StartNew(() => doOne(SelectedTask.option1));
var task2 = task1.ContinueWith(t => doOne(SelectedTask.option2));
// Block the current thread until the last task completes
task2.Wait();
The code in the title of your post (with a couple of fixes) is essentially performing the exact same function as my sequential task chain above:
Task.Factory.StartNew(() => doOne(SelectedTask.option1))
.ContinueWith(t => doOne(SelectedTask.option2))
.Wait();
Answer to your question below.
If I understand correctly, you want to be able to run a task for a variable list of SelectedTasks in parallel:
List<SelectedTask> selectedTaskOptions = new List<SelectedTask>()
{
SelectedTask.option1,
SelectedTask.option2,
SelectedTask.option3,
SelectedTask.option4,
SelectedTask.option5
};
RunAllSelectedTaskOptions(selectedTaskOptions);
RunAllSelectedTaskOptions to accept and run a list of SelectedTasks:
public void RunAllSelectedTaskOptions(List<SelectedTask> selectedTaskOptions)
{
List<Task> createdTasks = new List<Task>();
foreach(var taskOption in selectedTaskOptions)
{
createdTasks.Add(Task.Factory.CreateNew(() => doOne(taskOption)));
}
Task.WaitAll(createdTasks);
}
Another way of implementing RunAllSelectedTaskOptions would be to use Parallel.ForEach, which will execute in parallel and will block until the slowest/last iteration has completed:
public void RunAllSelectedTaskOptions(List<SelectedTask> selectedTaskOptions)
{
Parallel.ForEach(selectedTaskOptions, taskOption => doOne(taskOption));
}
I assume you are talking about parallelizing the two doOne calls?
If so, then you will need to do something like this:
var task1 = Task.Factory.StartNew(() => doOne(SelectedTask.option1));
var task2 = Task.Factory.StartNew(() => doOne(SelectedTask.option2));
var taskList = new List<Task>{task1, task2};
Task.WaitAll(taskList);
*The above code is fairly accurate but the syntax has not been validated.