-
-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathChannelProcessingExamples.cs
More file actions
237 lines (185 loc) · 8.74 KB
/
Copy pathChannelProcessingExamples.cs
File metadata and controls
237 lines (185 loc) · 8.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
#if NET6_0_OR_GREATER
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using EnumerableAsyncProcessor.Extensions;
namespace EnumerableAsyncProcessor.Example;
/// <summary>
/// Examples demonstrating channel-based processing capabilities
/// </summary>
public static class ChannelProcessingExamples
{
/// <summary>
/// Demonstrates basic channel-based processing with multiple consumers
/// </summary>
public static async Task BasicChannelProcessingExample()
{
Console.WriteLine("=== Basic Channel Processing Example ===");
var urls = new[]
{
"https://api.example.com/data/1",
"https://api.example.com/data/2",
"https://api.example.com/data/3",
"https://api.example.com/data/4",
"https://api.example.com/data/5",
"https://api.example.com/data/6"
};
// Create channel options with multiple consumers
var options = ChannelProcessorOptions.CreateUnbounded(consumerCount: 3);
var processedCount = 0;
// Process URLs with multiple consumers working in parallel
var processor = urls.ForEachWithChannelAsync(async url =>
{
// Simulate HTTP request
await Task.Delay(Random.Shared.Next(100, 500));
var count = Interlocked.Increment(ref processedCount);
Console.WriteLine($"[Consumer {Environment.CurrentManagedThreadId}] Processed {url} ({count}/{urls.Length})");
}, options);
var stopwatch = Stopwatch.StartNew();
await processor;
stopwatch.Stop();
Console.WriteLine($"Processed {urls.Length} URLs in {stopwatch.ElapsedMilliseconds}ms using multiple consumers");
}
/// <summary>
/// Demonstrates bounded channel with backpressure handling
/// </summary>
public static async Task BoundedChannelWithBackpressureExample()
{
Console.WriteLine("\n=== Bounded Channel with Backpressure Example ===");
var documents = Enumerable.Range(1, 20).Select(i => $"Document_{i:D3}.pdf").ToArray();
// Create bounded channel with limited capacity
var options = ChannelProcessorOptions.CreateBounded(
capacity: 3, // Small buffer to demonstrate backpressure
consumerCount: 1 // Single slow consumer
);
var processedDocs = new List<string>();
var lockObj = new object();
var processor = documents.ForEachWithChannelAsync(async doc =>
{
// Simulate slow document processing
Console.WriteLine($"Processing {doc}...");
await Task.Delay(200);
lock (lockObj)
{
processedDocs.Add(doc);
}
Console.WriteLine($"✓ Completed {doc} ({processedDocs.Count}/{documents.Length})");
}, options);
var stopwatch = Stopwatch.StartNew();
await processor;
stopwatch.Stop();
Console.WriteLine($"Processed {documents.Length} documents in {stopwatch.ElapsedMilliseconds}ms");
Console.WriteLine("Note: Producer was throttled by bounded channel capacity");
}
/// <summary>
/// Demonstrates result processing with streaming output
/// </summary>
public static async Task ResultProcessingWithStreamingExample()
{
Console.WriteLine("\n=== Result Processing with Streaming Example ===");
var numbers = Enumerable.Range(1, 10).ToArray();
// Process numbers and return results using multiple consumers
var options = ChannelProcessorOptions.CreateUnbounded(consumerCount: 4);
var processor = numbers.SelectWithChannelAsync(async number =>
{
// Simulate CPU-intensive calculation
await Task.Delay(Random.Shared.Next(50, 200));
var result = Math.Pow(number, 3); // Calculate cube
Console.WriteLine($"[Consumer {Environment.CurrentManagedThreadId}] {number}³ = {result}");
return new { Number = number, Cube = result };
}, options);
Console.WriteLine("Streaming results as they become available:");
var results = new List<object>();
await foreach (var result in processor.GetResultsAsyncEnumerable())
{
results.Add(result);
Console.WriteLine($"Got result: {result.Number}³ = {result.Cube}");
}
Console.WriteLine($"Total results received: {results.Count}");
}
/// <summary>
/// Demonstrates channel options configuration
/// </summary>
public static async Task ChannelOptionsConfigurationExample()
{
Console.WriteLine("\n=== Channel Options Configuration Example ===");
var tasks = Enumerable.Range(1, 15);
// Configure channel with specific options
var options = new ChannelProcessorOptions
{
Capacity = 5, // Bounded channel
FullMode = BoundedChannelFullMode.Wait, // Block producer when full
ConsumerCount = 3, // Multiple consumers
SingleWriter = true, // Optimize for single producer
SingleReader = false, // Multiple readers
AllowSynchronousContinuations = false // Use thread pool for continuations
};
var processedTasks = new List<int>();
var lockObj = new object();
var processor = tasks.ForEachWithChannelAsync(async task =>
{
Console.WriteLine($"[Consumer {Environment.CurrentManagedThreadId}] Starting task {task}");
await Task.Delay(100);
lock (lockObj)
{
processedTasks.Add(task);
}
Console.WriteLine($"[Consumer {Environment.CurrentManagedThreadId}] Completed task {task}");
}, options);
await processor;
Console.WriteLine($"Processed {processedTasks.Count} tasks with custom channel configuration");
Console.WriteLine($"Tasks processed: [{string.Join(", ", processedTasks.OrderBy(x => x))}]");
}
/// <summary>
/// Performance comparison between channel-based and batch processing
/// </summary>
public static async Task PerformanceComparisonExample()
{
Console.WriteLine("\n=== Performance Comparison Example ===");
var workItems = Enumerable.Range(1, 200).ToArray();
// Test channel-based processing
Console.WriteLine("Testing Channel-based processing...");
var channelOptions = ChannelProcessorOptions.CreateUnbounded(consumerCount: 4);
var channelStopwatch = Stopwatch.StartNew();
var channelProcessor = workItems.ForEachWithChannelAsync(async item =>
{
await Task.Delay(5); // Simulate work
}, channelOptions);
await channelProcessor;
channelStopwatch.Stop();
// Test traditional batch processing
Console.WriteLine("Testing Batch processing...");
var batchStopwatch = Stopwatch.StartNew();
var batchProcessor = workItems.ForEachAsync(async item =>
{
await Task.Delay(5); // Simulate work
}).ProcessInBatches(4);
await batchProcessor;
batchStopwatch.Stop();
// Compare results
Console.WriteLine($"Channel-based: {channelStopwatch.ElapsedMilliseconds}ms");
Console.WriteLine($"Batch-based: {batchStopwatch.ElapsedMilliseconds}ms");
Console.WriteLine($"Performance ratio: {(double)channelStopwatch.ElapsedMilliseconds / batchStopwatch.ElapsedMilliseconds:F2}x");
if (channelStopwatch.ElapsedMilliseconds < batchStopwatch.ElapsedMilliseconds * 1.2)
{
Console.WriteLine("✓ Channel-based processing is competitive with batch processing");
}
}
/// <summary>
/// Run all channel processing examples
/// </summary>
public static async Task RunAllExamples()
{
await BasicChannelProcessingExample();
await BoundedChannelWithBackpressureExample();
await ResultProcessingWithStreamingExample();
await ChannelOptionsConfigurationExample();
await PerformanceComparisonExample();
Console.WriteLine("\n=== All Channel Processing Examples Completed ===");
}
}
#endif