Sequence .Net Tasks - c#

I have several Task objects, such as Task<int>, Task<string>, Task<double>, that I want to run sequentially. That is, as each Task completes the next Task starts automatically. Each task depends on the output from the prior task.
How do I do this? The myTask.ContinueWith(...) overloads all assume a delegate parameter. But a Task is not a delegate.

You could do like:
var t1 = new Task(() => Console.WriteLine("Completed t1"));
var t2 = new Task(() => Console.WriteLine("Completed t2"));
var t3 = new Task(() => Console.WriteLine("Completed t3"));
t1.ContinueWith(t => t2.RunSynchronously())
.ContinueWith(t => t3.RunSynchronously());
t1.Start();
The example above only works, if you don't care about the results of the tasks. If you however (as your edit states), need to pass the result of each task to the next task, then you could do something like:
var t1 = new Task(() => Console.WriteLine("Completed t1"));
var t2 = new Task(() => Console.WriteLine("Completed t2"));
var t3 = new Task(() => Console.WriteLine("Completed t3"));
t1.ContinueWith(task1 =>
{
Console.WriteLine(task1.Result);
t2.ContinueWith(task2 =>
{
Console.WriteLine("{0} | {1}", task1.Result, task2.Result);
t3.ContinueWith(task3 =>
{
Console.WriteLine("{0} | {1} | {2}",
task1.Result, task2.Result, task3.Result);
});
t3.Start();
});
t2.Start();
});
t1.Start();
/* OUTPUT:
Completed t1
Completed t1 | Completed t2
Completed t1 | Completed t2 | Completed t3 */

Related

Task.ContinueWith query

I`m new to TPL and need some help in understanding .continueWith. Can you pls tell me what is wrong in first task-continuation and how is second one correct?
List<int> input = new List<int> { 1, 2, 3, 4, 5 };
//Gives cast error at the second continuation: cannot implicitly convert
//Task to Task<List<int>>
Task<List<int>> task = Task.Factory.StartNew<List<int>>(
(state) => { return (List<int>)state; }, input)
.ContinueWith<List<int>>(
(prevTask => { return (List<int>)prevTask.Result; }))
.ContinueWith(
(prevTask => { Console.WriteLine("All completed"); }));
//Works fine
Task<List<int>> task1 = Task.Factory.StartNew<List<int>>(
(state) => { return (List<int>)state; }, input);
task1.ContinueWith<List<int>>(
(prevTask => { return (List<int>)prevTask.Result; }))
.ContinueWith(
(prevTask => { Console.WriteLine("All completed"); }));
The first chain of method calls you made ends with ContinueWith() which returns a Task object which can't be assigned to a Task<List<int>>:
Task.Factory.StartNew<List<int>>(...) // returns Task<List<int>>
.ContinueWith<List<int>>(...) // returns Task<List<int>>
.ContinueWith(...); // returns Task
But in the second one, the result of the last ContinueWith is not assigned to anything, so works fine.
For the first one to work, either define task as Task:
Task task = Task.Factory.StartNew<List<int>>(
(state) => { return (List<int>)state; }, input)
.ContinueWith<List<int>>(
(prevTask => { return (List<int>)prevTask.Result; }))
.ContinueWith(
(prevTask => { Console.WriteLine("All completed"); }));
Or make the last call a generic one:
Task<List<int>> task = Task.Factory.StartNew<List<int>>(
(state) => { return (List<int>)state; }, input)
.ContinueWith<List<int>>(
(prevTask => { return (List<int>)prevTask.Result; }))
.ContinueWith<List<int>>(
(prevTask => { Console.WriteLine("All completed"); }));

Wait all tasks with some conditions

I am writing some complex tasks and I can't do success to solve the problem.
I open some tasks for searching some data asynchronously.
At the end I wait for all tasks.
I want to run on all the data and does not stop the tasks until they all finish.
But, when one of the tasks finds some relevant data, I want to continue with the code that is after the wait all tasks, but at the same time, I want to continue searching with my tasks (until all the data will be read).
Here I found a way how to continue the code after the wait all if one of the tasks finds the data, the problem is that it also stops all tasks from running.
Here is my code (this is just code for demonstration of the problem).
private static void RunTasks()
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
Task<bool> t0 = Task.Factory.StartNew<bool>(() => Find(1, 2));
Task<bool> t1 = Task.Factory.StartNew<bool>(() => Find(4, 7));
Task<bool> t2 = Task.Factory.StartNew<bool>(() => Find(13, 14));
t0.ContinueWith(_ =>
{
if (t0.Result)
tcs.TrySetResult(t0.Result);
});
t1.ContinueWith(_ =>
{
if (t1.Result)
tcs.TrySetResult(t1.Result);
});
t2.ContinueWith(_ =>
{
if (t2.Result)
tcs.TrySetResult(t2.Result);
});
tcs.Task.Wait();
Console.WriteLine("Found");
ContinueWork(); //Runs after at least one data found or when all tasks finish.
}
//Just for demonstration...
private static bool Find(int a, int b)
{
Console.WriteLine("a: " + a + " b: " + b);
return a == 4 && b == 7 ? true : false;
}
How can I write it so that when some data is found it will continue to ContinueWork method and also will continue with reading the data with the tasks?
Thanks.
This is not the cleanest way but it will serve your purposes:
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
Task<bool>[] tasks = new Task<bool>[3];
tasks[0] = Task.Factory.StartNew<bool>(() => Find(1, 2));
tasks[1] = Task.Factory.StartNew<bool>(() => Find(4, 7));
tasks[2] = Task.Factory.StartNew<bool>(() => Find(13, 14));
tasks[0].ContinueWith(_ =>
{
if (tasks[0].Result)
tcs.TrySetResult(tasks[0].Result);
});
tasks[1].ContinueWith(_ =>
{
if (tasks[1].Result)
tcs.TrySetResult(tasks[1].Result);
});
tasks[2].ContinueWith(_ =>
{
if (tasks[2].Result)
tcs.TrySetResult(tasks[2].Result);
});
Task.WaitAny(tasks);
Console.WriteLine("Found");
ContinueWork();

AttachedToParent Task confusion

I have a problem understanding how AttachedToParent parameter works.
Here is the sample code:
public static void Main(string[] args)
{
Task<int[]> parentTask = Task.Run(()=>
{
int[] results = new int[3];
Task t1 = new Task(() => { Thread.Sleep(3000); results[0] = 0; }, TaskCreationOptions.AttachedToParent);
Task t2 = new Task(() => { Thread.Sleep(3000); results[1] = 1; }, TaskCreationOptions.AttachedToParent);
Task t3 = new Task(() => { Thread.Sleep(3000); results[2] = 2; }, TaskCreationOptions.AttachedToParent);
t1.Start();
t2.Start();
t3.Start();
return results;
});
Task finalTask = parentTask.ContinueWith(parent =>
{
foreach (int result in parent.Result)
{
Console.WriteLine(result);
}
});
finalTask.Wait();
Console.ReadLine();
}
As I understand, when a Task has child Tasks, the parent Task finishes when all the child tasks are ready. The problem with this example is that the output looks like this:
0
0
0
This means that the parent Task was not waiting for its child tasks to finish. The only way to get a valid result 0 1 2 is to use Wait on all of the child Taks, by adding some piece of code like this before the return results; statement:
Task[] taskList = { t1, t2, t3 };
Task.WaitAll(taskList);
My question is this. Why do we use TaskCreationOptions.AttachedToParent when we also have to manually call Wait method for every child Task?
Edit:
While I was writing this question, I've changed code a little bit and now AttachedToParent works well. The only difference is that I've used parentTask.Start(); instead of the Task.Run();.
public static void Main(string[] args)
{
Task<int[]> parentTask = new Task<int[]>(()=>
{
int[] results = new int[3];
Task t1 = new Task(() => { Thread.Sleep(3000); results[0] = 0; }, TaskCreationOptions.AttachedToParent);
Task t2 = new Task(() => { Thread.Sleep(3000); results[1] = 1; }, TaskCreationOptions.AttachedToParent);
Task t3 = new Task(() => { Thread.Sleep(3000); results[2] = 2; }, TaskCreationOptions.AttachedToParent);
t1.Start();
t2.Start();
t3.Start();
//Task[] taskList = { t1, t2, t3 };
//Task.WaitAll(taskList);
return results;
});
parentTask.Start();
Task finalTask = parentTask.ContinueWith(parent =>
{
foreach (int result in parent.Result)
{
Console.WriteLine(result);
}
});
finalTask.Wait();
Console.ReadLine();
}
I still don't understand why there is a problem with the first example.
Look at this blog post: Task.Run vs Task.Factory.StartNew
The first example:
Task.Run(someAction);
is simplified equivalent of method:
Task.Factory.StartNew(someAction,
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);
I made little research, using reflector, here is a source of method Task.Run
public static Task Run(Func<Task> function, CancellationToken cancellationToken)
{
if (function == null)
throw new ArgumentNullException("function");
cancellationToken.ThrowIfSourceDisposed();
if (cancellationToken.IsCancellationRequested)
return Task.FromCancellation(cancellationToken);
else
return (Task) new UnwrapPromise<VoidTaskResult>(
(Task) Task<Task>.Factory.StartNew(function,
cancellationToken,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default),
true);
}
The important parameter of method Task.Factory.StartNew is TaskCreationOptions creationOptions. In method Task.Factory.StartNew that parameter equals TaskCreationOptions.DenyChildAttach. It mean that
an InvalidOperationException will be thrown if an attempt is made to
attach a child task to the created task
You need to change to TaskCreationOptions.None to achieve right behavior of code.
Method Task.Run does not provide ability to change TaskCreationOptions parameter.

Switch async Task to sync task

I have the following code:
Task.Factory.ContinueWhenAll(items.Select(p =>
{
return CreateItem(p);
}).ToArray(), completedTasks => { Console.WriteLine("completed"); });
Is it possible to convert ContinueWhenAll to a synchronous method? I want to switch back between async and sync.
Edit: I should metnion that each of the "tasks" in the continuewhenall method should be executing synchronously.
If you want to leave your existing code intact and have a variable option of executing synchronously you should make these changes:
bool isAsync = false; // some flag to check for async operation
var batch = Task.Factory.ContinueWhenAll(items.Select(p =>
{
return CreateItem(p);
}).ToArray(), completedTasks => { Console.WriteLine("completed"); });
if (!isAsync)
batch.Wait();
This way you can toggle it programmatically instead of by editing your source code. And you can keep the continuation code the same for both methods.
Edit:
Here is a simple pattern for having the same method represented as a synchronous and async version:
public Item CreateItem(string name)
{
return new Item(name);
}
public Task<Item> CreateItemAsync(string name)
{
return Task.Factory.StartNew(() => CreateItem(name));
}
Unless am mistaken this is what you're looking for
Task.WaitAll(tasks);
//continuation code here
i think you can try this.
using TaskContinuationOptions for a simple scenario.
var taskFactory = new TaskFactory(TaskScheduler.Defau
var random = new Random();
var tasks = Enumerable.Range(1, 30).Select(p => {
return taskFactory.StartNew(() => {
var timeout = random.Next(5, p * 50);
Thread.Sleep(timeout / 2);
Console.WriteLine(#" 1: ID = " + p);
return p;
}).ContinueWith(t => {
Console.WriteLine(#"* 2: ID = " + t.Result);
}, TaskContinuationOptions.ExecuteSynchronously);
}).ToArray();
Task.WaitAll(tasks);
or using TPL Dataflow for a complex scenario.
var step2 = new ActionBlock<int>(i => {
Thread.Sleep(i);
Console.WriteLine(#"* 2: ID = " + i);
}, new ExecutionDataflowBlockOptions {
MaxDegreeOfParallelism = 1,
//MaxMessagesPerTask = 1
});
var random = new Random();
var tasks = Enumerable.Range(1, 50).Select(p => {
return Task.Factory.StartNew(() => {
var timeout = random.Next(5, p * 50);
Thread.Sleep(timeout / 2);
Console.WriteLine(#" 1: ID = " + p);
return p;
}).ContinueWith(t => {
Thread.Sleep(t.Result);
step2.Post(t.Result);
});
}).ToArray();
await Task.WhenAll(tasks).ContinueWith(t => step2.Complete());
await step2.Completion;

Update UI from Task Parallel Library issue

I am learning the art of TPL. So I try to update UI in incremental fashion from TPL but I can't get it right. Here is the code I'm using:
int i = 0, flag = 5;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
while (i < flag)
{
Task.Factory.StartNew(() =>
{
this.Text = i.ToString();
//System.Threading.Thread.SpinWait(50000000);
}, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
i++;
System.Threading.Thread.Sleep(1000);
}
}, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
I am trying to update windows title bar UI in incremental fashion. The code runs without exceptions but the UI is not updated until i's value become 5. I want to show it like; 1, then 2 and so on until i is 5.
When I am running my program I noticed that my program hangs until the task is completed or finished.
What am I doing wrong in respect of updating the UI when using tasks?
another approach 1
Task.Factory.StartNew(() =>
{
Parallel.For(1, 6, i =>
{
System.Threading.Thread.SpinWait(500000000); do work here
BeginInvoke((Action)delegate { this.Text = i.ToString(); });
});
});
another approach 2
int i = 0, flag = 5;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
while (i < flag)
{
Task.Factory.StartNew(() =>
{
this.Text = i.ToString();
}, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
i++;
System.Threading.Thread.SpinWait(50000000); // do work here
}
}); // <---- Removed arguments (specifically uiScheduler)
You're telling the task to use the uiScheduler context when starting the initial task. Remove that and your code should be fine.
int i = 0, flag = 5;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
while (i < flag)
{
Task.Factory.StartNew(() =>
{
this.Text = i.ToString();
}, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
i++;
System.Threading.Thread.Sleep(1000);
}
}); // <---- Removed arguments (specifically uiScheduler)

Categories

Resources