-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Expand file tree
/
Copy pathProgram.cs
More file actions
155 lines (134 loc) · 4.28 KB
/
Program.cs
File metadata and controls
155 lines (134 loc) · 4.28 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
using System.Collections.Concurrent;
// Verification entry point
ExecutionContextCaptureDemo();
await TaskRunExample.ProcessOnUIThread();
// Install a custom SynchronizationContext for the demo
var demoContext = new DemoSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(demoContext);
SyncContextExample.DoWork();
demoContext.ProcessQueue();
SynchronizationContext.SetSynchronizationContext(null);
await Task.Delay(200);
Console.WriteLine("Done.");
// <ExecutionContextCapture>
static void ExecutionContextCaptureDemo()
{
// Capture the current ExecutionContext
ExecutionContext? ec = ExecutionContext.Capture();
// Later, run a delegate within that captured context
if (ec is not null)
{
ExecutionContext.Run(ec, _ =>
{
// Code here sees the ambient state from the point of capture
Console.WriteLine("Running inside captured ExecutionContext.");
}, null);
}
}
// </ExecutionContextCapture>
static class SingleThreadSynchronizationContext
{
public static Task Run(Func<Task> asyncAction)
{
var previousContext = SynchronizationContext.Current;
var context = new SingleThreadContext();
SynchronizationContext.SetSynchronizationContext(context);
Task task;
try
{
task = asyncAction();
task.ContinueWith(_ => context.Complete(), TaskScheduler.Default);
context.RunOnCurrentThread();
return task;
}
finally
{
SynchronizationContext.SetSynchronizationContext(previousContext);
}
}
private sealed class SingleThreadContext : SynchronizationContext
{
private readonly BlockingCollection<(SendOrPostCallback Callback, object? State)> _queue = new();
public override void Post(SendOrPostCallback d, object? state) => _queue.Add((d, state));
public void RunOnCurrentThread()
{
foreach (var workItem in _queue.GetConsumingEnumerable())
{
workItem.Callback(workItem.State);
}
}
public void Complete() => _queue.CompleteAdding();
}
}
// <SyncContextUsage>
static class SyncContextExample
{
public static void DoWork()
{
// Capture the current SynchronizationContext
SynchronizationContext? sc = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(_ =>
{
// ... do work on the ThreadPool ...
if (sc is not null)
{
sc.Post(_ =>
{
// This runs on the original context (e.g. UI thread)
Console.WriteLine("Back on the original context.");
}, null);
}
});
}
}
// </SyncContextUsage>
// Minimal SynchronizationContext for demo purposes
sealed class DemoSynchronizationContext : SynchronizationContext
{
private readonly Queue<(SendOrPostCallback, object?)> _queue = new();
public override void Post(SendOrPostCallback d, object? state)
{
lock (_queue)
{
_queue.Enqueue((d, state));
}
}
public void ProcessQueue()
{
Thread.Sleep(150); // Allow time for ThreadPool work to complete
lock (_queue)
{
while (_queue.Count > 0)
{
var (callback, state) = _queue.Dequeue();
callback(state);
}
}
}
}
// <TaskRunExample>
static class TaskRunExample
{
public static async Task ProcessOnUIThread()
{
// Assume this method is called from a UI thread.
// Task.Run offloads work to the thread pool.
string result = await Task.Run(async () =>
{
string data = await DownloadAsync();
// Compute runs on the thread pool, not the UI thread,
// because SynchronizationContext doesn't flow into Task.Run.
return Compute(data);
});
// Back on the UI thread (captured by the outer await).
Console.WriteLine(result);
}
private static async Task<string> DownloadAsync()
{
await Task.Delay(100);
return "downloaded data";
}
private static string Compute(string data) =>
$"Computed: {data.Length} chars";
}
// </TaskRunExample>