What is Task.Current? - c#

This question (Is there a way to tell which Tasks are currently running in Task Parallel Library?) says that you can access the currently running Task by the Task.Current property. But I can't find any such property in the documentation.
Why is this?

What would you want Task.Current to be, had it been there? Consider this:
async Task DoAsync()
{
var currentTask = Task.Current; // imagine Task.Current does exist
await Task.Delay(1000);
}
// ...
var task = Task.Run(() => DoAsync());
Would you want currentTask to be the task returned by Task.Run or by DoAsync?
Perhaps, a less contrived question would be this:
How to access the Task object from the task's own action?
You certainly cannot do it the following way, as it has a race condition:
Task task = null;
task = Task.Run(() => Console.WriteLine({ task.Id }));
You can however do it this way:
Task task = null;
task = new Task(() => Console.WriteLine({ task.Id }));
task.Run();
Is it what you're looking for? In my experience, I only once needed something like this:
Task sequencing and re-entracy

Related

How to run multiple methods in parallel in ASP.NET

I have 8 methods in an ASP.NET Console App, like Fun1(), Fun2(), Fun3() ... and so on. In my console application I have called all these methods sequentially. But now the requirement is I have do that using parallel programming concepts. I have read about task and threading concepts in Java but completely new to .NET parallel Programming.
Here is the flow of methods I needed in my console app,
As you can see the diagram, Task1 and Task2 should run in parallel, and Task3 will only occur after completion of the previous two.
The functions inside each task, for example Fun3 and Fun4 for the Task1, should run sequentially, the one after the other.
Can anyone please help me out?
One way to solve this is, by using WhenAll.
To take an example, I have created X number of methods with the name FuncX() like this:
async static Task<int> FuncX()
{
await Task.Delay(500);
var result = await Task.FromResult(1);
return result;
}
In this case, we have Func1, Func3, Func4, Func5, and Func6.
So we call methods and pass them to a list of Task.
var task1 = new List<Task<int>>();
task1.Add(Func3());
task1.Add(Func4());
var task2 = new List<Task<int>>();
task2.Add(Func1());
task2.Add(Func5());
task2.Add(Func6());
You have 2 options to get the result:
// option1
var eachFunctionIsDoneWithAwait1 = await Task.WhenAll(task1);
var eachFunctionIsDoneWithAwait2 = await Task.WhenAll(task2);
var sum1 = eachFunctionIsDoneWithAwait1.Sum() + eachFunctionIsDoneWithAwait2.Sum();
Console.WriteLine(sum1);
// option2
var task3 = new List<List<Task<int>>>();
task3.Add(task1);
task3.Add(task2);
var sum2 = 0;
task3.ForEach(async x =>
{
var r = await Task.WhenAll(x);
sum2 += r.Sum();
});
Console.WriteLine(sum2);
This is just example for inspiration, you can change it and do it the way you want.
Here is how you could create the tasks according to the diagram, using the Task.Run method:
Task task1 = Task.Run(() =>
{
Fun3();
Fun4();
});
Task task2 = Task.Run(() =>
{
Fun1();
Fun5();
Fun6();
});
Task task3 = Task.Run(async () =>
{
await Task.WhenAll(task1, task2);
Fun7();
Fun8();
});
The Task.Run invokes the delegate on the ThreadPool, not on a dedicated thread. If you have some reason to create a dedicated thread for each task, you could use the advanced Task.Factory.StartNew method with the TaskCreationOptions.LongRunning argument, as shown here.
It should be noted that the above implementation has not an optimal behavior in case of exceptions. In case the Fun3() fails immediately, the optimal behavior would be to stop the execution of the task2 as soon as possible. Instead this implementation will execute all three functions Fun1, Fun5 and Fun6 before propagating the error. You could fix this minor flaw by creating a CancellationTokenSource and invoking the Token.ThrowIfCancellationRequested after each function, but it's going to be messy.
Another issue is that in case both task1 and task2 fail, only the exception of the task1 is going to be propagated through the task3. Solving this issue is not trivial.
Update: Here is one way to solve the issue of partial exception propagation:
Task task3 = Task.WhenAll(task1, task2).ContinueWith(t =>
{
if (t.IsFaulted)
{
TaskCompletionSource tcs = new();
tcs.SetException(t.Exception.InnerExceptions);
return tcs.Task;
}
if (t.IsCanceled)
{
TaskCompletionSource tcs = new();
tcs.SetCanceled(new TaskCanceledException(t).CancellationToken);
return tcs.Task;
}
Debug.Assert(t.IsCompletedSuccessfully);
Fun7();
Fun8();
return Task.CompletedTask;
}, default, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default)
.Unwrap();
In case both task1 and task2 fail, the task3 will propagate the exceptions of both tasks.

How do I "WaitAll" on multiple spawned Task ConfigureAwait tasks?

I have a C# console app that spawns multiple task threads. I need to wait for all of them to finish before exiting the app otherwise it closes all the threads. How can I do a ".WaitAll()" on the ConfiguredTaskAwaiter (I'm assuming that's what I want to watch)
foreach (var entityType in entityTypes)
{
// Create a task for each entity to thread the calls
var task = Task.Run(() => TestTaskAsync(entityType)).ConfigureAwait(false); // Long running process
var awaiter = task.GetAwaiter();
Action showComplete = () => OnComplete($"PROCESSING FOR ENTITY ({entityType.Id}) HAS COMPLETED.");
awaiter.OnCompleted(showComplete);
}
// Should not continue until all tasks have completed
Configure await does not change any property of the task itself, it just tells the compiler whether you care about your current context after you await the task. Therefore
var task = Task.Delay(1000);
// ...
await task.ConfigureAwait(false);
is identical to
var task = Task.Delay(1000).ConfigureAwait(false);
// ...
await task;
In your specific case, it makes no sense to configure await for tasks you're not actually planning to await. You should aggregate them and await them all together instead.
What you probably want to do is
var tasks = new List<Task>();
foreach (var type in types)
{
var task = Task.Run(() =>
{
TestTaskAsync(entityType);
OnComplete($"PROCESSING FOR ENTITY ({entityType.Id}) HAS COMPLETED.");
});
tasks.Add(task);
}
await Task.WhenAll(tasks).ConfigureAwait(false);
This tells the compiler that you do not care what context you will have after the await. Since you are not awaiting the individual tasks, it makes no sense to worry about context after awaiting those. You will only be awaiting a task that represents them all, that is the task returned by Task.WhenAll.
I also took the liberty of putting the continuation in the callback. If you do not want to do that for any reason, feel free to split it the way you have in your question, it will work the same way.
var taskList = new List<Task>();
foreach (var entityType in entityTypes)
{
....
taskList.Add(task);
}
Task.WaitAll(taskList.ToArray())
but you should then async version called WhenAll :
await Task.WhenAll(taskList)

How do I stop awaiting a task but keep the task running in background?

If I have a list of tasks which I want to execute together but at the same time I want to execute a certain number of them together, so I await for one of them until one of them finishes, which then should mean awaiting should stop and a new task should be allowed to start, but when one of them finishes, I don't know how to stop awaiting for the task which is currently being awaited, I don't want to cancel the task, just stop awaiting and let it continue running in the background.
I have the following code
foreach (var link in SharedVars.DownloadQueue)
{
if (currentRunningCount != batch)
{
var task = DownloadFile(extraPathPerLink, link, totalLen);
_ = task.ContinueWith(_ =>
{
downloadQueueLinksTasks.Remove(task);
currentRunningCount--;
// TODO SHOULD CHANGE WHAT IS AWAITED
});
currentRunningCount++;
downloadQueueLinksTasks.Add(task);
}
if (currentRunningCount == batch)
{
// TODO SHOULD NOT AWAIT 0
await downloadQueueLinksTasks[0];
}
}
I found about Task.WhenAny but from this comment here I understood that the other tasks will be ignored so it's not the solution I want to achieve.
I'm sorry if the question is stupid or wrong but I can't seem to find any information related on how to solve it, or what is the name of the operation I want to achieve so I can even search correctly.
Solution Edit
All the answers provided are correct, I accepted the one I decided to use but still all of them are correct.
Thank you everyone, I learned a lot from all of you from these different answers and different ways to approach the problem and how to think about it.
What I learned about this specific problem was that I still needed to await for the other tasks left, so the solution was to have the Task.WhenAny inside the loop (which returns the finished task (this is also important)) AND Task.WhenAll outside the loop to await the other left tasks.
Task.WhenAny returns the Task which completed.
foreach (var link in SharedVars.DownloadQueue)
{
var task = DownloadFile(extraPathPerLink, link, totalLen);
downloadQueueLinksTasks.Add(task);
if (downloadQueueLinksTasks.Count == batch)
{
// Wait for any Task to complete, then remove it from
// the list of pending tasks.
var completedTask = await Task.WhenAny(downloadQueueLinksTasks);
downloadQueueLinksTasks.Remove(completedTask);
}
}
// Wait for all of the remaining Tasks to complete
await Task.WhenAll(downloadQueueLinksTasks);
You can use Task.WaitAny()
Here is the demonstration of the behavior:
public static async Task Main(string[] args)
{
IList<Task> tasks = new List<Task>();
tasks.Add(TestAsync(0));
tasks.Add(TestAsync(1));
tasks.Add(TestAsync(2));
tasks.Add(TestAsync(3));
tasks.Add(TestAsync(4));
tasks.Add(TestAsync(5));
var result = Task.WaitAny(tasks.ToArray());
Console.WriteLine("returned task id is {0}", result);
///do other operations where
//before exiting wait for other tasks so that your tasks won't get cancellation signal
await Task.WhenAll(tasks.ToArray());
}
public static async Task TestAsync(int i)
{
Console.WriteLine("Staring to wait" + i);
await Task.Delay(new Random().Next(1000, 10000));
Console.WriteLine("Task finished" + i);
}
Output:
Staring to wait0
Staring to wait1
Staring to wait2
Staring to wait3
Staring to wait4
Staring to wait5
Task finished0
returned task id is 0
Task finished4
Task finished2
Task finished1
Task finished5
Task finished3
What you're asking about is throttling, which for asynchronous code is best expressed via SemaphoreSlim:
var semaphore = new SemaphoreSlim(batch);
var tasks = SharedVars.DownloadQueue.Select(link =>
{
await semaphore.WaitAsync();
try { return DownloadFile(extraPathPerLink, link, totalLen); }
finally { semaphore.Release(); }
});
var results = await Task.WhenAll(tasks);
You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive and add using System.Reactive.Linq; - then you can do this:
IDisposable subscription =
SharedVars.DownloadQueue
.ToObservable()
.Select(link =>
Observable.FromAsync(() => DownloadFile(extraPathPerLink, link, totalLen)))
.Merge(batch) //max concurrent downloads
.Subscribe(file =>
{
/* process downloaded file here
(no longer a task) */
});
If you need to stop the downloads before they would naturally finish just call subscription.Dispose().

Is async and unwrap necessary for StartNew()?

I have this code:
var task = Task.Factory.StartNew(() => service.StartAsync(ct), ct);
but I'm wondering if it should instead be this:
var task = Task.Factory.StartNew(async () => await service.StartAsync(ct), ct).Unwrap();
Is the first one correct to start my async service? Or is the second one better?
Consider the type of task returned, the first one yields Task<Task<int>> while the second yields Task<int>. So really the first one is a Task representing starting of the inner task, while the second, unwrapped, represents the Task returned by the inner method representing the service starting. Finally you can also Unwrap the first and get the same effect without the async/await which is unnecessary here. None of this really covers what the need for StartNew is at all in this case just reviews the return types your looking at.
Consider the following code:
public class AsyncTesting
{
public void StartServiceTest()
{
Task<Task<int>> tsk1 = Task.Factory.StartNew(() => StartAsync());
Task<int> tsk2 = Task.Factory.StartNew(() => StartAsync()).Unwrap();
Task<int> tsk3 = Task.Factory.StartNew(async () => await StartAsync()).Unwrap();
}
public Task<int> StartAsync() => Task.Delay(2500).ContinueWith(tsk => 1);
}
The method that does not Unwrap returns a Task that represents starting the internal Task not the work it does.
As JSteward explain in their answer, the first line of code is wrong. It doesn't do what you expect it to do:
Task<Task> task = Task.Factory.StartNew(() => service.StartAsync(ct), ct); // Buggy
The second line has the correct behavior, but not because of the async/await. The async/await can be safely elided. What makes it correct is the Unwrap. It is still problematic though, because it violates the guideline CA2008 about not creating tasks without passing a TaskScheduler.
The best way to solve your problem is to use the Task.Run method:
Task task = Task.Run(() => service.StartAsync(ct), ct); // Correct
You can read about the differences between Task.Run and Task.Factory.StartNew in this article by Stephen Toub.

Serializing async task and async continuation

I have long running processing that I want to perform in a background task. At the end of the task, I want to signal that it has completed. So essentially I have two async tasks that I want to run in the background, one after the other.
I am doing this with continuations, but the continuation is starting prior to the initial task completing. The expected behavior is that the continuation run only after the initial task has completed.
Here is some sample code that demonstrates the problem:
// Setup task and continuation
var task = new Task(async () =>
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
});
task.ContinueWith(async (x) =>
{
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
});
task.Start();
The DebugLog function:
static void DebugLog(string s, params object[] args)
{
string tmp = string.Format(s, args);
System.Diagnostics.Debug.WriteLine("{0}: {1}", DateTime.Now.ToString("HH:mm:ss.ffff"), tmp);
}
Expected Output:
TASK Starting
TASK Finishing
CONTINUATION Starting
CONTINUATION Ending
Actual Output:
TASK Starting
CONTINUATION Starting
CONTINUATION Ending
TASK Finishing
Again, my question is why is the continuation starting prior to the completion of the initial task? How do I get the continuation to run only after the completion of the first task?
Workaround #1
I can make the above code work as expected if I make the intial task synchronous - that is if I Wait on the Task.Delay like so:
var task = new Task(() =>
{
DebugLog("TASK Starting");
Task.Delay(1000).Wait(); // Wait instead of await
DebugLog("TASK Finishing");
});
For many reasons, it is bad to use Wait like this. One reason is that it blocks the thread, and this is something I want to avoid.
Workaround #2
If I take the task creation and move it into it's own function, that seems to work as well:
// START task and setup continuation
var task = Test1();
task.ContinueWith(async (x) =>
{
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
});
static public async Task Test1()
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
}
Credit for the above approach goes to this somewhat related (but not duplicate) question: Use an async callback with Task.ContinueWith
Workaround #2 is better than Workaround #1 and is likely the approach I'll take if there is no explanation for why my initial code above does not work.
Your original code is not working because the async lambda is being translated into an async void method underneath, which has no built-in way to notify any other code that it has completed. So, what you're seeing is the difference between async void and async Task. This is one of the reasons that you should avoid async void.
That said, if it's possible to do with your code, use Task.Run instead of the Task constructor and Start; and use await rather than ContinueWith:
var task = Task.Run(async () =>
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
});
await task;
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
Task.Run and await work more naturally with async code.
You shouldn't be using the Task constructor directly to start tasks, especially when starting async tasks. If you want to offload work to be executed in the background use Task.Run instead:
var task = Task.Run(async () =>
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
});
About the continuation, it would be better to just append it's logic to the end of the lambda expression. But if you're adamant on using ContinueWith you need to use Unwrap to get the actual async task and store the it so you could handle exceptions:
task = task.ContinueWith(async (x) =>
{
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
}).Unwrap();
try
{
await task;
}
catch
{
// handle exceptions
}
Change your code to
// Setup task and continuation
var t1 = new Task(() =>
{
Console.WriteLine("TASK Starting");
Task.Delay(1000).Wait(); // actual work here
Console.WriteLine("TASK Finishing");
});
var t2 = t1.ContinueWith((x) =>
{
Console.WriteLine("C1");
Task.Delay(100).Wait(); // actual work here
Console.WriteLine("C2");
});
t1.Start();
// Exception will be swallow without the line below
await Task.WhenAll(t1, t2);

Categories

Resources