CSharp parallel programming without blocking 2


In my last post I briefly went over parallel programming initial steps but did not get into the nitty gritty as one would expect. No doubt Microsoft's article about it is great. Still as humans I think it is always better to read more than one book to understand and process the information correctly.
With the current .net6 LTS version the C# 10 is used as of this writing
That's that for the introduction. I have segregated this article into three different ways of performing it.
Before continuing any further please note that almost all ways should work when the main thread is running before and after the snippet is completed execution like say in an ASP .Net
server instance. There were issues running them in a Console
type or instance as the main thread finished completion all others will end or similar.
Also, important to note that if TaskScheduler.Current == TaskScheduler.Default
, then the main thread will not wait for the sub threads. One can add a simple check like below.
if(TaskScheduler.Current == TaskScheduler.Default){
Console.WriteLine("Main will not wait!");
}
else {
Console.WriteLine("Main will wait!");
}
Using Parallel.ForEachAsync
This is the easiest way to do it. I a using the ReadLinesAsync
for this demo. Notice how the last write line is printed first demonstrating the asynchronous operation.
public async Task Load() {
var cncl = new CancellationTokenSource();
var res = System.IO.File.ReadLinesAsync("/tmp/file.txt", System.Text.Encoding.UTF8, cncl.Token);
// this is the only way I was able to run this bit after the main thread exited
Parallel.ForEachAsync(res, (line, cncl) =>
{
Console.WriteLine("Line is " + line);
return default;
});
Console.WriteLine("Main finished now.");
}
Using Threads
This is somewhat the next easiest way to do it. Make sure the thread's IsBackground
is set to false
otherwise say if the Load()
dies after calling Start()
, Start()
will not complete to execute. Read more here .
public async Task Load() {
// this worked nicely, with thr running even after server pocess was killed
var th = new ThreadStart(() => {
Thread.Sleep(10_000);
Console.WriteLine("Thr finished now.");
});
var thr = new Thread(th);
thr.IsBackground = false;
thr.Start();
Console.WriteLine("Main finished now.");
}
Using Task.Run()
This is easy as well but with a lot of options like using the TaskContinuationOptions . This is the recommended approach that I would suggest, say like using TaskContinuationOptions.OnlyOnFaulted
to catch any exceptions without needing to use the try - catch
at all. This is similar to the Promise syntax in Javascript and other languages. If one has used it in any of those languages, it should be fairly an easy pop to gulp if I may.
public async Task Load() {
async void Load_()
{
var res = System.IO.File.ReadLinesAsync("/tmp/file.txt", System.Text.Encoding.UTF8, cancellationToken.Token);
await foreach (var line in res)
{
Console.WriteLine("Line is " + line);
}
}
Task.Run(() => Load_())
.ContinueWith(ts => {
_logger.LogInformation("Finished loading now ...");
throw new ApplicationException("Some app excpn here ...");
// }, TaskContinuationOptions.OnlyOnRanToCompletion)
})
.ContinueWith(ex => {
_logger.LogError("Issue loading ... " + ex.Exception.Message);
}, TaskContinuationOptions.OnlyOnFaulted);
Console.WriteLine("Main finished now.");
}