Skip to content

Commit 682159c

Browse files
committed
Add support for NET Standard 2.1
1 parent c291b01 commit 682159c

47 files changed

Lines changed: 328 additions & 462 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AsyncEnumerable.nuspec

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
33
<metadata>
44
<id>AsyncEnumerator</id>
5-
<version>3.0.2-beta</version>
6-
<authors>sergiis,Dasync</authors>
7-
<licenseUrl>https://raw.githubusercontent.com/Dasync/AsyncEnumerable/master/LICENSE</licenseUrl>
5+
<version>3.1.0</version>
6+
<authors>sergiis,dasync</authors>
7+
<license type="expression">MIT</license>
88
<projectUrl>https://github.com/Dasync/AsyncEnumerable</projectUrl>
99
<requireLicenseAcceptance>false</requireLicenseAcceptance>
1010
<iconUrl>https://raw.githubusercontent.com/Dasync/AsyncEnumerable/master/icon.png</iconUrl>
@@ -22,7 +22,7 @@ the thread (the processing is scheduled on a worker thread instead).
2222

2323
EXAMPLE
2424

25-
using System.Collections.Async;
25+
using Dasync.Collections;
2626

2727
static IAsyncEnumerable&lt;int&gt; ProduceAsyncNumbers(int start, int end)
2828
{
@@ -73,7 +73,7 @@ static void ConsumeNumbers()
7373
}</description>
7474
<summary>Introduces IAsyncEnumerable, IAsyncEnumerator, ForEachAsync(), and ParallelForEachAsync()</summary>
7575
<releaseNotes>
76-
3.0.0-beta1: Add support for NET Core App 3.0, consolidate interface with Microsoft's implementation.
76+
3.1.0: Add support for NET Standard 2.1, consolidate interface with Microsoft's implementation.
7777
2.2.0: New extension methods: SelectMany, Append, Prepend, OfType, Concat, Distinct, ToDictionaryAsync, ToLookupAsync, AggregateAsync.
7878
2.1.0: New extension methods: Batch, UnionAll, Single, SingleOrDefault, DefaultIfEmpty, Cast.
7979
2.0.0: Revise design of the library: same features, but slight paradigm shift and interface breaking changes.
@@ -95,18 +95,18 @@ static void ConsumeNumbers()
9595
</group>
9696
<group targetFramework=".NETStandard2.0">
9797
</group>
98-
<group targetFramework=".NETCoreApp3.0">
98+
<group targetFramework=".NETStandard2.1">
9999
</group>
100100
</dependencies>
101101
</metadata>
102102
<files>
103-
<file src="NetFramework.4.5\lib\bin\Release\AsyncEnumerable.dll" target="lib\net45\AsyncEnumerable.dll" />
104-
<file src="NetFramework.4.5\lib\bin\Release\AsyncEnumerable.xml" target="lib\net45\AsyncEnumerable.xml" />
105-
<file src="NetStandard.1.4\lib\bin\Release\netstandard1.4\AsyncEnumerable.dll" target="lib\netstandard1.4\AsyncEnumerable.dll" />
106-
<file src="NetStandard.1.4\lib\bin\Release\netstandard1.4\AsyncEnumerable.xml" target="lib\netstandard1.4\AsyncEnumerable.xml" />
107-
<file src="NetStandard.1.4\lib\bin\Release\netstandard2.0\AsyncEnumerable.dll" target="lib\netstandard2.0\AsyncEnumerable.dll" />
108-
<file src="NetStandard.1.4\lib\bin\Release\netstandard2.0\AsyncEnumerable.xml" target="lib\netstandard2.0\AsyncEnumerable.xml" />
109-
<file src="NetCoreApp.3.0\lib\bin\Release\netcoreapp3.0\AsyncEnumerable.dll" target="lib\netcoreapp3.0\AsyncEnumerable.dll" />
110-
<file src="NetCoreApp.3.0\lib\bin\Release\netcoreapp3.0\AsyncEnumerable.xml" target="lib\netcoreapp3.0\AsyncEnumerable.xml" />
103+
<file src="netfx45\lib\bin\Release\AsyncEnumerable.dll" target="lib\net45\AsyncEnumerable.dll" />
104+
<file src="netfx45\lib\bin\Release\AsyncEnumerable.xml" target="lib\net45\AsyncEnumerable.xml" />
105+
<file src="src\bin\Release\netstandard1.4\AsyncEnumerable.dll" target="lib\netstandard1.4\AsyncEnumerable.dll" />
106+
<file src="src\bin\Release\netstandard1.4\AsyncEnumerable.xml" target="lib\netstandard1.4\AsyncEnumerable.xml" />
107+
<file src="src\bin\Release\netstandard2.0\AsyncEnumerable.dll" target="lib\netstandard2.0\AsyncEnumerable.dll" />
108+
<file src="src\bin\Release\netstandard2.0\AsyncEnumerable.xml" target="lib\netstandard2.0\AsyncEnumerable.xml" />
109+
<file src="src\bin\Release\netstandard2.1\AsyncEnumerable.dll" target="lib\netstandard2.1\AsyncEnumerable.dll" />
110+
<file src="src\bin\Release\netstandard2.1\AsyncEnumerable.xml" target="lib\netstandard2.1\AsyncEnumerable.xml" />
111111
</files>
112112
</package>

AsyncEnumerable.sln

Lines changed: 18 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 16
44
VisualStudioVersion = 16.0.28606.126
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsyncEnumerable", "NetFramework.4.5\lib\AsyncEnumerable.csproj", "{111536C3-DABF-48CF-B993-E504C0BC5CE7}"
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsyncEnumerable.NetFx45", "netfx45\lib\AsyncEnumerable.NetFx45.csproj", "{111536C3-DABF-48CF-B993-E504C0BC5CE7}"
77
EndProject
88
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".NET Framework 4.5", ".NET Framework 4.5", "{6D19B0B6-35B3-4A6B-B060-60DD1E7F8CAB}"
99
EndProject
10-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.NetFramework.4.5", "NetFramework.4.5\tests\Tests.NetFramework.4.5.csproj", "{9557379C-26E0-448F-AB22-B197885B05E2}"
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsyncEnumerable.Tests.NetFx45", "netfx45\tests\AsyncEnumerable.Tests.NetFx45.csproj", "{9557379C-26E0-448F-AB22-B197885B05E2}"
1111
EndProject
12-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".NET Standard 1.4", ".NET Standard 1.4", "{FEFF0146-6799-40F0-A9CF-AB5AAB6584C9}"
12+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".NET Standard", ".NET Standard", "{FEFF0146-6799-40F0-A9CF-AB5AAB6584C9}"
1313
EndProject
14-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncEnumerable", "NetStandard.1.4\lib\AsyncEnumerable.csproj", "{2299336C-BC95-41DB-8697-5127E3754370}"
15-
EndProject
16-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.NetStandard.1.4", "NetStandard.1.4\tests\Tests.NetStandard.1.4.csproj", "{24E51531-A113-4149-A32D-8B503DE9074B}"
14+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncEnumerable", "src\AsyncEnumerable.csproj", "{2299336C-BC95-41DB-8697-5127E3754370}"
1715
EndProject
1816
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{6E8E24A4-11BF-46D5-B060-E993E070ED65}"
1917
ProjectSection(SolutionItems) = preProject
@@ -22,11 +20,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{6E8E24
2220
README.md = README.md
2321
EndProjectSection
2422
EndProject
25-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".NET Core App 3.0", ".NET Core App 3.0", "{6ED76ABC-17C0-4345-A51B-B872D84C7E00}"
26-
EndProject
27-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncEnumerable", "NetCoreApp.3.0\lib\AsyncEnumerable.csproj", "{9924C6BB-88C7-4202-BB6D-24A282177C22}"
28-
EndProject
29-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.NetCoreApp.3.0", "NetCoreApp.3.0\tests\Tests.NetCoreApp.3.0.csproj", "{B19B17DB-017F-45E1-B574-D734E6747E21}"
23+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncEnumerable.Tests", "Tests\AsyncEnumerable.Tests.csproj", "{FFD079F6-2A1E-4D81-BE07-005DD274D336}"
3024
EndProject
3125
Global
3226
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -74,42 +68,18 @@ Global
7468
{2299336C-BC95-41DB-8697-5127E3754370}.Release|x64.Build.0 = Release|Any CPU
7569
{2299336C-BC95-41DB-8697-5127E3754370}.Release|x86.ActiveCfg = Release|Any CPU
7670
{2299336C-BC95-41DB-8697-5127E3754370}.Release|x86.Build.0 = Release|Any CPU
77-
{24E51531-A113-4149-A32D-8B503DE9074B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
78-
{24E51531-A113-4149-A32D-8B503DE9074B}.Debug|Any CPU.Build.0 = Debug|Any CPU
79-
{24E51531-A113-4149-A32D-8B503DE9074B}.Debug|x64.ActiveCfg = Debug|Any CPU
80-
{24E51531-A113-4149-A32D-8B503DE9074B}.Debug|x64.Build.0 = Debug|Any CPU
81-
{24E51531-A113-4149-A32D-8B503DE9074B}.Debug|x86.ActiveCfg = Debug|Any CPU
82-
{24E51531-A113-4149-A32D-8B503DE9074B}.Debug|x86.Build.0 = Debug|Any CPU
83-
{24E51531-A113-4149-A32D-8B503DE9074B}.Release|Any CPU.ActiveCfg = Release|Any CPU
84-
{24E51531-A113-4149-A32D-8B503DE9074B}.Release|Any CPU.Build.0 = Release|Any CPU
85-
{24E51531-A113-4149-A32D-8B503DE9074B}.Release|x64.ActiveCfg = Release|Any CPU
86-
{24E51531-A113-4149-A32D-8B503DE9074B}.Release|x64.Build.0 = Release|Any CPU
87-
{24E51531-A113-4149-A32D-8B503DE9074B}.Release|x86.ActiveCfg = Release|Any CPU
88-
{24E51531-A113-4149-A32D-8B503DE9074B}.Release|x86.Build.0 = Release|Any CPU
89-
{9924C6BB-88C7-4202-BB6D-24A282177C22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
90-
{9924C6BB-88C7-4202-BB6D-24A282177C22}.Debug|Any CPU.Build.0 = Debug|Any CPU
91-
{9924C6BB-88C7-4202-BB6D-24A282177C22}.Debug|x64.ActiveCfg = Debug|Any CPU
92-
{9924C6BB-88C7-4202-BB6D-24A282177C22}.Debug|x64.Build.0 = Debug|Any CPU
93-
{9924C6BB-88C7-4202-BB6D-24A282177C22}.Debug|x86.ActiveCfg = Debug|Any CPU
94-
{9924C6BB-88C7-4202-BB6D-24A282177C22}.Debug|x86.Build.0 = Debug|Any CPU
95-
{9924C6BB-88C7-4202-BB6D-24A282177C22}.Release|Any CPU.ActiveCfg = Release|Any CPU
96-
{9924C6BB-88C7-4202-BB6D-24A282177C22}.Release|Any CPU.Build.0 = Release|Any CPU
97-
{9924C6BB-88C7-4202-BB6D-24A282177C22}.Release|x64.ActiveCfg = Release|Any CPU
98-
{9924C6BB-88C7-4202-BB6D-24A282177C22}.Release|x64.Build.0 = Release|Any CPU
99-
{9924C6BB-88C7-4202-BB6D-24A282177C22}.Release|x86.ActiveCfg = Release|Any CPU
100-
{9924C6BB-88C7-4202-BB6D-24A282177C22}.Release|x86.Build.0 = Release|Any CPU
101-
{B19B17DB-017F-45E1-B574-D734E6747E21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
102-
{B19B17DB-017F-45E1-B574-D734E6747E21}.Debug|Any CPU.Build.0 = Debug|Any CPU
103-
{B19B17DB-017F-45E1-B574-D734E6747E21}.Debug|x64.ActiveCfg = Debug|Any CPU
104-
{B19B17DB-017F-45E1-B574-D734E6747E21}.Debug|x64.Build.0 = Debug|Any CPU
105-
{B19B17DB-017F-45E1-B574-D734E6747E21}.Debug|x86.ActiveCfg = Debug|Any CPU
106-
{B19B17DB-017F-45E1-B574-D734E6747E21}.Debug|x86.Build.0 = Debug|Any CPU
107-
{B19B17DB-017F-45E1-B574-D734E6747E21}.Release|Any CPU.ActiveCfg = Release|Any CPU
108-
{B19B17DB-017F-45E1-B574-D734E6747E21}.Release|Any CPU.Build.0 = Release|Any CPU
109-
{B19B17DB-017F-45E1-B574-D734E6747E21}.Release|x64.ActiveCfg = Release|Any CPU
110-
{B19B17DB-017F-45E1-B574-D734E6747E21}.Release|x64.Build.0 = Release|Any CPU
111-
{B19B17DB-017F-45E1-B574-D734E6747E21}.Release|x86.ActiveCfg = Release|Any CPU
112-
{B19B17DB-017F-45E1-B574-D734E6747E21}.Release|x86.Build.0 = Release|Any CPU
71+
{FFD079F6-2A1E-4D81-BE07-005DD274D336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
72+
{FFD079F6-2A1E-4D81-BE07-005DD274D336}.Debug|Any CPU.Build.0 = Debug|Any CPU
73+
{FFD079F6-2A1E-4D81-BE07-005DD274D336}.Debug|x64.ActiveCfg = Debug|Any CPU
74+
{FFD079F6-2A1E-4D81-BE07-005DD274D336}.Debug|x64.Build.0 = Debug|Any CPU
75+
{FFD079F6-2A1E-4D81-BE07-005DD274D336}.Debug|x86.ActiveCfg = Debug|Any CPU
76+
{FFD079F6-2A1E-4D81-BE07-005DD274D336}.Debug|x86.Build.0 = Debug|Any CPU
77+
{FFD079F6-2A1E-4D81-BE07-005DD274D336}.Release|Any CPU.ActiveCfg = Release|Any CPU
78+
{FFD079F6-2A1E-4D81-BE07-005DD274D336}.Release|Any CPU.Build.0 = Release|Any CPU
79+
{FFD079F6-2A1E-4D81-BE07-005DD274D336}.Release|x64.ActiveCfg = Release|Any CPU
80+
{FFD079F6-2A1E-4D81-BE07-005DD274D336}.Release|x64.Build.0 = Release|Any CPU
81+
{FFD079F6-2A1E-4D81-BE07-005DD274D336}.Release|x86.ActiveCfg = Release|Any CPU
82+
{FFD079F6-2A1E-4D81-BE07-005DD274D336}.Release|x86.Build.0 = Release|Any CPU
11383
EndGlobalSection
11484
GlobalSection(SolutionProperties) = preSolution
11585
HideSolutionNode = FALSE
@@ -118,9 +88,7 @@ Global
11888
{111536C3-DABF-48CF-B993-E504C0BC5CE7} = {6D19B0B6-35B3-4A6B-B060-60DD1E7F8CAB}
11989
{9557379C-26E0-448F-AB22-B197885B05E2} = {6D19B0B6-35B3-4A6B-B060-60DD1E7F8CAB}
12090
{2299336C-BC95-41DB-8697-5127E3754370} = {FEFF0146-6799-40F0-A9CF-AB5AAB6584C9}
121-
{24E51531-A113-4149-A32D-8B503DE9074B} = {FEFF0146-6799-40F0-A9CF-AB5AAB6584C9}
122-
{9924C6BB-88C7-4202-BB6D-24A282177C22} = {6ED76ABC-17C0-4345-A51B-B872D84C7E00}
123-
{B19B17DB-017F-45E1-B574-D734E6747E21} = {6ED76ABC-17C0-4345-A51B-B872D84C7E00}
91+
{FFD079F6-2A1E-4D81-BE07-005DD274D336} = {FEFF0146-6799-40F0-A9CF-AB5AAB6584C9}
12492
EndGlobalSection
12593
GlobalSection(ExtensibilityGlobals) = postSolution
12694
SolutionGuid = {98CBAF14-E09D-4B95-ABA5-0028ADD420B1}

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2019 D-ASYNC LLC
3+
Copyright (c) 2019 D-ASYNC
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

NUGET-DOC.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
__1: How to use this library?__
2+
3+
See examples above. You can the core code and lots of useful extension methods in the `Dasync.Collections` namespace.
4+
5+
6+
__2: Using CancellationToken__
7+
8+
```csharp
9+
using Dasync.Collections;
10+
11+
IAsyncEnumerable<int> ProduceNumbers()
12+
{
13+
return new AsyncEnumerable<int>(async yield => {
14+
15+
await FooAsync(yield.CancellationToken);
16+
});
17+
}
18+
```
19+
20+
__3: Always remember about ConfigureAwait(false)__
21+
22+
To avoid performance degradation and possible dead-locks in ASP.NET or WPF applications (or any `SynchronizationContext`-dependent environment), you should always put `ConfigureAwait(false)` in your `await` statements:
23+
24+
```csharp
25+
using Dasync.Collections;
26+
27+
IAsyncEnumerable<int> GetValues()
28+
{
29+
return new AsyncEnumerable<int>(async yield =>
30+
{
31+
await FooAsync().ConfigureAwait(false);
32+
33+
// Yes, it's even needed for 'yield.ReturnAsync'
34+
await yield.ReturnAsync(123).ConfigureAwait(false);
35+
});
36+
}
37+
```
38+
39+
__4: Clean-up on incomplete enumeration__
40+
41+
Imagine such situation:
42+
43+
```csharp
44+
using Dasync.Collections;
45+
46+
IAsyncEnumerable<int> ReadValuesFromQueue()
47+
{
48+
return new AsyncEnumerable<int>(async yield =>
49+
{
50+
using (var queueClient = CreateQueueClient())
51+
{
52+
while (true)
53+
{
54+
var message = queueClient.DequeueMessageAsync();
55+
if (message == null)
56+
break;
57+
58+
await yield.ReturnAsync(message.Value);
59+
}
60+
}
61+
});
62+
}
63+
64+
Task<int> ReadFirstValueOrDefaultAsync()
65+
{
66+
return ReadValuesFromQueue().FirstOrDefaultAsync();
67+
}
68+
```
69+
70+
The `FirstOrDefaultAsync` method will try to read first value from the `IAsyncEnumerator`, and then will just dispose it. However, disposing `AsyncEnumerator` does not mean that the `queueClient` in the lambda function will be disposed automatically as well, because async methods are just state machines which need somehow to go to a particular state to do the clean-up.
71+
To provide such behavior, when you dispose an `AsyncEnumerator` before you reach the end of enumeration, it will tell to resume your async lambda function (at `await yield.ReturnAsync()`) with the `AsyncEnumerationCanceledException` (derives from `OperationCanceledException`). Having such exception in your lambda method will break normal flow of enumeration and will go to terminal state of the underlying state machine, what will do the clean-up, i.e. dispose the `queueClient` in this case. You don't need (and shouldn't) catch that exception type, because it's handled internally by `AsyncEnumerator`. The same exception is thrown when you call `yield.Break()`.
72+
73+
There is another option to do the cleanup on `Dispose`:
74+
75+
```csharp
76+
using Dasync.Collections;
77+
78+
IAsyncEnumerator<int> GetQueueEnumerator()
79+
{
80+
var queueClient = CreateQueueClient();
81+
82+
return new AsyncEnumerable<int>(async yield =>
83+
{
84+
while (true)
85+
{
86+
var message = queueClient.DequeueMessageAsync();
87+
if (message == null)
88+
break;
89+
90+
await yield.ReturnAsync(message.Value);
91+
}
92+
},
93+
onDispose: () => queueClient.Dispose());
94+
}
95+
```
96+
97+
__5: Why is GetAsyncEnumeratorAsync async?__
98+
99+
The `IAsyncEnumerable.GetAsyncEnumeratorAsync()` method is async and returns a `Task<IAsyncEnumerator>`, where the current implementation of `AsyncEnumerable` always runs that method synchronously and just returns an instance of `AsyncEnumerator`. Having interfaces allows you to do your own implementation, where classes mentioned above are just helpers. The initial idea was to be able to support database-like scenarios, where `GetAsyncEnumeratorAsync()` executes a query first (what internally returns a pointer), and the `MoveNextAsync()` enumerates through rows (by using that pointer).
100+
101+
__6: Returning IAsyncEnumerable vs IAsyncEnumerator__
102+
103+
When you implement a method that returns an async-enumerable collection you have a choice to return either `IAsyncEnumerable` or `IAsyncEnumerator` - the constructors of the helper classes `AsyncEnumerable` and `AsyncEnumerator` are absolutely identical. Both interfaces have same set of useful extension methods, like `ForEachAsync`.
104+
105+
When you create an 'enumerable', you create a factory that produces 'enumerators', i.e. you can enumerate through a collection many times. On the other hand, creating an 'enumerator' is needed when you can through a collection only once.
106+
107+
__7: Where is Reset or ResetAsync?__
108+
109+
The `Reset` method must not be on the `IEnumerator` interface, and should be considered as deprecated. Create a new enumerator instead. This is the reason why the 'oneTimeUse' flag was removed in version 2 of this library.
110+
111+
__8: How can I do synchronous for-each enumeration through IAsyncEnumerable?__
112+
113+
You can use extension methods like `IAsyncEnumerable.ToEnumerable()` to use built-in `foreach` enumeration, BUT you should never do that! The general idea of this library is to avoid thread-blocking calls on worker threads, where converting an `IAsyncEnumerable` to `IEnumerable` will just defeat the whole purpose of this library. This is the reason why such synchronous extension methods are marked with `[Obsolete]` attribute.
114+
115+
__9: What's the difference between ForEachAsync and ParallelForEachAsync?__
116+
117+
The `ForEachAsync` allows you to go through a collection and perform an action on every single item in sequential manner. On the other hand, `ParallelForEachAsync` allows you to run the action on multiple items at the same time where the sequential
118+
order of completion is not guaranteed. For the latter, the degree of the parallelism is controlled by the `maxDegreeOfParalellism`
119+
argument, however it does not guarantee to spin up the exact amount of threads, because it depends on the [thread pool size](https://msdn.microsoft.com/en-us/library/system.threading.threadpool.setmaxthreads(v=vs.110).aspx) and its occupancy at a moment of time. Such parallel approach is much better than trying to create a task for an action for every single item on the collection and then awaiting on all of them with `Task.WhenAll`, because it adds less overhead to the runtime, better with memory usage, and helps with throttling-sensitive scenarios.

0 commit comments

Comments
 (0)