My code:
var r = from x in new Task<int>(() => 1)
from y in new Task<int>(() => x + 1)
select y;
r.ContinueWith(x => Console.WriteLine(x.Result)).Start();
or
new Task<int>(() => 1)
.ContinueWith(x => x.Result + 1)
.ContinueWith(x => Console.WriteLine(x.Result))
.Start();
Exception:
Start may not be called on a continuation task.
So I need to start the first task. Is there any way to call last task Start method to run all tasks?
Any reason not to use Task.Factory.StartNewmsdn, ms docs for the first task? Yes, it's inconsistent - but it's fundamentally a different kind of task, in terms of being started explicitly rather than just as a continuation.
I'm not really sure what's wrong with just writing this:
var t1 = new Task<int>(() => 1)
var r = from x in t1
from y in new Task<int>(() => x + 1)
select y;
r.ContinueWith(x => Console.WriteLine(x.Result));
t1.Start();
or this:
var t = new Task<int>(() => 1)
t.ContinueWith(x => x.Result + 1)
.ContinueWith(x => Console.WriteLine(x.Result))
t.Start();
That directly expresses what you actually want to do. (It's the initial task that you want to kick off. So what's wrong with invoking Start on that initial task?) Why are you looking for a syntax that obscures that?
EDIT: fixed first example...
EDIT 2 to add:
So I realise now that LinqToTasks expects task selectors to return running tasks. So the second from clause in your first example returns a task that nothing will ever run. So what you actually need is this:
var t1 = new Task<int>(() => 1);
var r = from x in t1
from y in Task<int>.Factory.StartNew(() => x + 1)
select y;
r.ContinueWith(x => Console.WriteLine(x.Result));
t1.Start();
Nothing else is going to call Start on the tasks produced in these from clauses. Since the relevant selectors don't actually get executed until the previous task completes, you're still in control of when to kick off the root task.
That appears to work, but it's pretty ugly. But that appears to be how LinqToTasks is designed... I think I'd stick with the regular function call syntax.
The problem is that selecting tasks with LINQ Will Only Create an Expression Tree!
So here's what you need to do:
var query =
from i in Enumerable.Range(1, 4)
let task = Task.Factory.StartNew(() => Tuple.Create(i, IsPrime(i))) // put a breakpoint here
select task.ContinueWith(delegate {
Console.WriteLine("{0} {1} prime.", _.Result.Item1, _.Result.Item2 ? "is" : "is not");
});
// breakpoint never hit yet
query.ToArray(); // breakpoint hit here 4 times
// all tasks are now running and continuations will start
TaskEx.Await(query.ToArray()); // breakpoint hit 4 more times!!
I had the same problem today. I wanted to create a wrapper task that handles an error from an inner task. This is what I came up with:
var yourInitialTask = new Task(delegate
{
throw e;
});
var continuation = task.ContinueWith(t =>
{
if (task.IsCanceled)
{
Debug.WriteLine("IsCanceled: " + job.GetType());
}
else if (task.IsFaulted)
{
Debug.WriteLine("IsFaulted: " + job.GetType());
}
else if (task.IsCompleted)
{
Debug.WriteLine("IsCompleted: " + job.GetType());
}
}, TaskContinuationOptions.ExecuteSynchronously); //or consider removing execute synchronously if your continuation task is going to take long
var wrapper = new Task(() =>
{
task.Start();
continuation.Wait();
});
return wrapper;
The key features here are that
-the continuation part runs after the original task, as you want
-the wrapper is Startable. Continuation tasks created with ContineWith() are nto Startable.
A less key feature of this example, is that the exception is being logged and discarded (solves my problem, not yours). You might want to be doing something different when exceptions occur in the continuation such as rethrow it as an exception of the current task, so that it bubbles out.
As far as I'm aware, there's no sensible way to compose non-started tasks provided by the framework. The simplest solution I can think of is extension methods. Here are some examples which you could build on if you need this functionality.
Warning: Just as with passing around and composing tons of lambdas, if you find yourself needing these, it often means you are missing a type in your design that would simplify your code. Ask yourself what you gained by creating the subtasks.
/// <summary>
/// Compose tasks without starting them.
/// Waiting on the returned task waits for both components to complete.
/// An exception in the first task will stop the second task running.
/// </summary>
public static class TaskExtensions
{
public static Task FollowedBy(this Task first, Task second)
{
return FollowedBy(first,
() =>
{
second.Start();
second.Wait();
});
}
public static Task FollowedBy(this Task first, Action second)
{
return new Task(
() =>
{
if (first.Status == TaskStatus.Created) first.Start();
first.Wait();
second();
});
}
public static Task FollowedBy<T>(this Task first, Task<T> second)
{
return new Task<T>(
() =>
{
if (first.Status == TaskStatus.Created) first.Start();
first.Wait();
second.Start();
return second.Result;
});
}
public static Task FollowedBy<T>(this Task<T> first, Action<T> second)
{
return new Task(
() =>
{
if (first.Status == TaskStatus.Created) first.Start();
var firstResult = first.Result;
second(firstResult);
});
}
public static Task<TSecond> FollowedBy<TFirst, TSecond>(this Task<TFirst> first, Func<TFirst, TSecond> second)
{
return new Task<TSecond>(
() =>
{
if (first.Status == TaskStatus.Created) first.Start();
return second(first.Result);
});
}
}
The answer is simple. ContinueWith is automatically start task. And first task need to be running.
var r= Task<int>.Run<int>( () => 1 )
.ContinueWith<int>( x => x.Result + 1 )
.ContinueWith( x => Console.WriteLine( x.Result ) );
ContinueWith return task that start with checking previous task is done or not. This code work in the same way like below code
var firstTask = new Task<int>( () => 1 );
firstTask.Start();
var firstawaiter = firstTask.GetAwaiter();
var secondTask = new Task<int>( x => (int)x + 1 , firstawaiter.GetResult());
firstawaiter.OnCompleted( () =>
{
secondTask.Start();
} );
var secondawaiter = secondTask.GetAwaiter();
var thirdTask = new Task( x => Console.WriteLine( x ) , secondawaiter.GetResult());
secondawaiter.OnCompleted( () =>
{
thirdTask.Start();
} );
So, If first task is not completed, next task will not be started.
And you not need to start continuewith block .
Related
We had something like below
List<string> uncheckItems = new List<string>();
for (int i = 0; i < 100; i++)
{
uncheckItems.Add($"item {i + 1}");
}
var tasks = uncheckItems.Select(item =>
new Task(async () => await ProcessItem(item))
);
// Do some preparations
foreach (var task in tasks)
{
task.Start();
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine("=====================================================All finished");
It seems to make sense but the program never able to reach the all finished line.
And if I adjust the workflow to run tasks immediately like remove the task.Start() loop and change to
var tasks = uncheckItems.Select(async item =>
await ProcessItem(item)
);
Then it works.
However, I wonder
Why it stucks?
Is there any way we can keep the workflow(create tasks without trigger them directly and start them later on) and still able to utilize WaitAll()?
The reason is the lazy enumeration evaluation, you are starting different tasks than waiting with Task.WaitAll. This can be fixed for example with next:
var tasks = uncheckItems.Select(item =>
new Task(async () => await ProcessItem(item))
)
.ToArray();
Though it will not achieve your goal (as I understand) of waiting all ProcessItem to finish. You can do something like new Task(() => ProcessItem(item).GetAwaiter().GetResult()) but I think it would be better to change your approach, for example make ProcessItem return a "cold" task or using your second snippet and moving tasks creation to the point where they needed to be started.
You should be next to the world expert in Task to be using the constructor. The documentation warns against that:
This constructor should only be used in advanced scenarios where it is required that the creation and starting of the task is separated.
Rather than calling this constructor, the most common way to instantiate a Task object and launch a task is by calling the static Task.Run(Action) or TaskFactory.StartNew(Action) method.
If a task with no action is needed just for the consumer of an API to have something to await, a TaskCompletionSource should be used.
The Task constructor produces a non-started Task that will only start when Task.Start() is invoked, as you discovered.
The Task constructor also receives an Action (or Action<T>), so the return value is ignored. That means that, after started, the task will end as soon as async () => await ProcessItem(item) yields.
What you need is:
await Task.WhenAll(Enumerable.Range(0, 100).Select(i => ProcessItem($"item {i + 1}"));
Or, if you really have to block:
Task
.WhenAll(Enumerable.Range(0, 100).Select(i => ProcessItem($"item {i + 1}"))
.GetAwaiter().GetResult();
Get the select out of there.
List<string> uncheckItems = new List<string>();
for (int i = 0; i < 100; i++)
{
uncheckItems.Add($"item {i + 1}");
}
var tasks = new List<Task>();
foreach(var item in uncheckedItems) {
tasks.Add(Task.Run(() => ProcessItem(item)));
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine("========All finished");
https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.waitall?view=net-6.0
In the docs for TPL I found this line:
Invoke multiple continuations from the same antecedent
But this isn't explained any further. I naively assumed you could chain ContinueWiths in a pattern matching like manner until you hit the right TaskContinuationOptions.
TaskThatReturnsString()
.ContinueWith((s) => Console.Out.WriteLine(s.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith((f) => Console.Out.WriteLine(f.Exception.Message), TaskContinuationOptions.OnlyOnFaulted)
.ContinueWith((f) => Console.Out.WriteLine("Cancelled"), TaskContinuationOptions.OnlyOnCanceled)
.Wait();
But this doesn't work like I hoped for at least two reasons.
The continuations are properly chained so the 2nd ContinueWith gets the result form the 1st, that is implemented as new Task, basically the ContinueWith task itself. I realize that the String could be returned onwards, but won't that be a new task with other info lost?
Since the first option is not met, the Task is just cancelled. Meaning that the second set will never be met and the exceptions are lost.
So what do they mean in the docs when they say multiple continuations from the same antecedent?
Is there a proper patter for this or do we just have to wrap the calls in try catch blocks?
EDIT
So I guess this was what I was hoping I could do, note this is a simplified example.
public void ProccessAllTheThings()
{
var theThings = util.GetAllTheThings();
var tasks = new List<Task>();
foreach (var thing in theThings)
{
var task = util.Process(thing)
.ContinueWith((t) => Console.Out.WriteLine($"Finished processing {thing.ThingId} with result {t.Result}"), TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith((t) => Console.Out.WriteLine($"Error on processing {thing.ThingId} with error {t.Exception.Message}"), TaskContinuationOptions.OnlyOnFaulted);
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
}
Since this wasn't possible I was thinking I would have to wrap each task call in a try catch inside the loop so I wouldn't stop the process but not wait on it there. I wasn't sure what the correct way.
Sometimes a solution is just staring you in the face, this would work wouldn't it?
public void ProccessAllTheThings()
{
var theThings = util.GetAllTheThings();
var tasks = new List<Task>();
foreach (var thing in theThings)
{
var task = util.Process(thing)
.ContinueWith((t) =>
{
if (t.Status == TaskStatus.RanToCompletion)
{
Console.Out.WriteLine($"Finished processing {thing.ThingId} with result {t.Result}");
}
else
{
Console.Out.WriteLine($"Error on processing {thing.ThingId} - {t.Exception.Message}");
}
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
}
What you did is to create a sequential chain of multiple tasks.
What you need to do is attach all your continuation tasks to the first one:
var firstTask = TaskThatReturnsString();
var t1 = firstTask.ContinueWith (…);
var t2 = firstTask.ContinueWith (…);
var t3 = firstTask.ContinueWith (…);
Then you need to wait for all the continuation tasks:
Task.WaitAll (t1, t2, t3);
I've got multiple System.Threading.Timer which starts in parallel. In the end, I have got a Task.Wait to wait till all tasks are done. But it doesn't wait for all, how can I make it wait for all?
private List<Task> todayTasks = new List<Task>();
foreach (var item in todayReport)
{
todayTasks.Add(SetupTimer(item.Exec_Time, item.Report_Id));
}
Task.WaitAll(todayTasks.ToArray());
--SetupTimer--
private Task SetupTimer(DateTime alertTime, int id)
{
DateTime current = DateTime.Now;
TimeSpan timeToGo = alertTime.TimeOfDay - current.TimeOfDay;
if (timeToGo < TimeSpan.Zero) {
//TODO: ERROR time already passed
}
ExecCustomReportService executeCustom = new ExecCustomReportService();
return Task.Run(
() => new Timer(
x => executeCustom.AdhockReport(id), null, timeToGo, Timeout.InfiniteTimeSpan
)
);
}
You're really better off using a tool that is suited to the job. I'd suggest Microsoft's Reactive Framework (Rx). Then you can do this:
var query =
from item in todayReport.ToObservable()
from report in Observable.Start(() => executeCustom.AdhockReport(item.Report_Id))
select report;
IDisposable subscription =
query
.Subscribe(
report =>
{
/* Do something with each report */
},
() =>
{
/* Do something when finished */
});
You just need to NuGet "System.Reactive".
As #YacoubMassad stated in the comment, your task is merely creating the timer and returns.
What you could do is get rid of the timer and use Task.Delay:
return Task.Delay(timeToGo).ContinueWith(t=> executeCustom.AdhockReport(id));
I have following code:
public void Func()
{
...
var task = httpClient.PostAsync(...);
var onlyOnRanToCompletionTask = task
.ContinueWith(
t => OnPostAsyncSuccess(t, notification, provider, account.Name),
TaskContinuationOptions.OnlyOnRanToCompletion);
var onlyOnFaultedTask = task
.ContinueWith(
t => OnPostAsyncAggregateException(t, notification.EntityId),
TaskContinuationOptions.OnlyOnFaulted);
return true;
}
Func is not async and I would like to have something like fire and forget but with continuation function. I don't want to have that function async. The scenario is that this function is called in some kind of loop for group of objects to handle them. For me the problem seems that e.g. onlyOnRanToCompletionTask can be deleted when we finish Func execution.
Thank you in advance!
But your code should be performed as expected. Simple example:
void Main()
{
HandlingMyFuncAsync();
Console.WriteLine("Doing some work, while 'fire and forget job is performed");
Console.ReadLine();
}
public void HandlingMyFuncAsync()
{
var task = MyFuncAsync();
task.ContinueWith(t => Console.WriteLine(t), TaskContinuationOptions.OnlyOnRanToCompletion);
}
public async Task<string> MyFuncAsync()
{
await Task.Delay(5000);
return "A";
}
produces
Doing some work, while 'fire and forget job is performed
[end after 5 sec]
A
If I were you I'd look at using Microsoft's Reactive Framework (Rx) for this. Rx is designed for this kind of thing.
Here's some basic code:
void Main()
{
int[] source = new [] { 1, 2, 3 };
IObservable<int> query =
from s in source.ToObservable()
from u in Observable.Start(() => Func(s))
select s;
IDisposable subscription =
query
.Subscribe(
x => Console.WriteLine($"!{x}!"),
() => Console.WriteLine("Done."));
}
public void Func(int x)
{
Console.WriteLine($"+{x} ");
Thread.Sleep(TimeSpan.FromSeconds(1.0));
Console.WriteLine($" {x}-");
}
When I run that I get this kind of output:
+1
+2
+3
1-
!1!
2-
!2!
3-
!3!
Done.
It triggers off all of the calls and waits for the results to come in. It also let's you know when it is done.
Here's a more practical example showing how to use async and Rx to return a result:
void Main()
{
int[] source = new [] { 1, 2, 3 };
var query =
from s in source.ToObservable()
from u in Observable.FromAsync(() => Func(s))
select new { s, u };
IDisposable subscription =
query
.Subscribe(
x => Console.WriteLine($"!{x.s},{x.u}!"),
() => Console.WriteLine("Done."));
}
public async Task<int> Func(int x)
{
Console.WriteLine($"+{x} ");
await Task.Delay(TimeSpan.FromSeconds(1.0));
Console.WriteLine($" {x}-");
return 10 * x;
}
That gives:
+1
+2
+3
1-
!1,10!
3-
2-
!2,20!
!3,30!
Done.
Without seeing your full code I can't easily give you a complete example that you can use, but Rx will also handle opening an closing on your DB contexts using the Observable.Using operator.
You can get Rx by Nugetting "System.Reactive".
I need to call asynchronously number of delegates for the same function.
The question is how i should treat the call back function ?
we have couple of delegates running so CallbackMethod doesn't mean that that all async delegates finished.
AsyncMethodCaller c = new AsyncMethodCaller(instance.dummyMethod);
for (int i = 0; i < 100; i++)
{
IAsyncResult res = c.BeginInvoke(5000,
out dummy,
new AsyncCallback(CallbackMethod),
"executed on thread {0}, with result value \"{1}\".");
}
I would consider looking at using Tasks provided with TPL.
var task1 = Task.Run(() => instance.dummyMethod)
.ContinueWith((completedTask) => Console.WriteLine("Each callback here. Result: " + completedTask.Result));
// Blocks calling thread until all tasks are done.
Task.WaitAll(new [] { task1, task2 });
The WaitAll all ensures that all Tasks are done before continuing on the main thread. The above allows you to implement individual callbacks.
Alternatively use a single callback when All the async methods are complete:
Task.Factory.ContinueWhenAll(new [] { task1, task2 },
(allTasks) => Console.WriteLine("Callback when all are done."));
I would be inclined to use Microsoft's Reactive Framework (NuGet "Rx-Main") for this as it would be so much simpler.
Here's the code:
var query =
from n in Observable.Range(0, 100)
from dummy in Observable.Start(() => instance.dummyMethod())
select dummy;
query
.Subscribe(
dummy =>
{
Console.WriteLine(dummy.ToString());
},
() =>
{
Console.WriteLine("Done.");
});
The .Subscribe method has a callback for each dummy value produced as well as a "done" callback when all the values have been returned.
The query runs on multiple threads automatically for you.