Skip to content

Commit 40fe992

Browse files
committed
Added structured concurrency
1 parent db7adb4 commit 40fe992

File tree

6 files changed

+132
-0
lines changed

6 files changed

+132
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Contains all of my examples from various blog posts. You can find a comprehensiv
44

55
| BlogPost | Publish Date |
66
| -------------------------------------------------------------------------------------------------------------- | ------------ |
7+
| [Structured Concurrency in C#](StructuredConcurrency/) | 11.10.2023 |
78
| [Building a Minimal ASP.NET Core clone](AspNetCoreFromScratch/) | 14.09.2023 |
89
| [Enabling List<T> to store large amounts of elements](ChunkedList/) | 07.09.2023 |
910
| [The combined power of F# and C#](FSharpCombined/) | 30.07.2023 |

StructuredConcurrency/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Structured Concurrency in C#
2+
3+
Did you ever hear about *"Structured Concurrency"*? If not, this article is for you. We will discover what it is, why it is useful, and what it could look like in C#.
4+
5+
Found [here](https://steven-giesel.com/blogPost/59e57336-7c73-472f-a781-b0b79f0d47ad)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StructuredConcurrency", "StructuredConcurrency\StructuredConcurrency.csproj", "{DEC85AAC-D35C-4078-A565-5794DAD5A561}"
4+
EndProject
5+
Global
6+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7+
Debug|Any CPU = Debug|Any CPU
8+
Release|Any CPU = Release|Any CPU
9+
EndGlobalSection
10+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
11+
{DEC85AAC-D35C-4078-A565-5794DAD5A561}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
12+
{DEC85AAC-D35C-4078-A565-5794DAD5A561}.Debug|Any CPU.Build.0 = Debug|Any CPU
13+
{DEC85AAC-D35C-4078-A565-5794DAD5A561}.Release|Any CPU.ActiveCfg = Release|Any CPU
14+
{DEC85AAC-D35C-4078-A565-5794DAD5A561}.Release|Any CPU.Build.0 = Release|Any CPU
15+
EndGlobalSection
16+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.Diagnostics;
2+
3+
var sw = Stopwatch.StartNew();
4+
var tasks = TaskScope.Create(group =>
5+
{
6+
group.Run(async token => await Task.Delay(100, token));
7+
group.Run(async token => await Task.Delay(200, token));
8+
});
9+
10+
// This runs 200 ms
11+
await tasks;
12+
Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds} ms");
13+
14+
sw.Restart();
15+
var failedTasks = TaskScope.Create(group =>
16+
{
17+
group.Run(async token =>
18+
{
19+
await Task.Delay(100, token);
20+
throw new Exception("Boooommm!!!");
21+
});
22+
group.Run(async token => await Task.Delay(1000, token));
23+
});
24+
25+
// Runs 100 ms as the first task fails and cancels the rest.
26+
// Also bubbles up the exception.
27+
try
28+
{
29+
await failedTasks;
30+
}
31+
catch (Exception ex)
32+
{
33+
Console.WriteLine(ex.Message);
34+
}
35+
36+
// This runs 100 ms
37+
Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds} ms");
38+
39+
// Prints True
40+
Console.WriteLine(failedTasks.IsFaulted);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net7.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System.Collections.Concurrent;
2+
3+
public class TaskScope
4+
{
5+
private readonly CancellationTokenSource _cts = new();
6+
private readonly ConcurrentBag<Task> _tasks = new();
7+
8+
private TaskScope() { }
9+
10+
public static async Task Create(Func<TaskScope, Task> action)
11+
{
12+
await using var scope = new TaskScope();
13+
await action(scope);
14+
await scope.WaitForAll();
15+
}
16+
17+
public static async Task Create(Action<TaskScope> action)
18+
{
19+
await using var scope = new TaskScope();
20+
action(scope);
21+
await scope.WaitForAll();
22+
}
23+
24+
public async ValueTask DisposeAsync()
25+
{
26+
_cts.Cancel();
27+
await WaitForAll();
28+
}
29+
30+
public Task Run(Func<CancellationToken, Task> action)
31+
{
32+
var task = Task.Run(async () =>
33+
{
34+
try
35+
{
36+
await action(_cts.Token);
37+
}
38+
catch (Exception ex) when (ex is not OperationCanceledException)
39+
{
40+
_cts.Cancel();
41+
throw;
42+
}
43+
});
44+
45+
_tasks.Add(task);
46+
return task;
47+
}
48+
49+
private async Task WaitForAll()
50+
{
51+
try
52+
{
53+
await Task.WhenAll(_tasks.ToArray());
54+
}
55+
catch (Exception ex) when (ex is not OperationCanceledException)
56+
{
57+
throw;
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)