Created
June 2, 2017 14:13
-
-
Save svick/7307338f950ef972cbf4d57efad81713 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Threading; | |
using System.Threading.Tasks; | |
class Program | |
{ | |
static void Main() | |
{ | |
new Program().MainAsync().Wait(); | |
} | |
private int stayedOnContext = 0; | |
async Task MainAsync() | |
{ | |
SynchronizationContext.SetSynchronizationContext(new DedicatedThreadSynchronisationContext()); | |
var n = 10000; | |
for (int i = 0; i < n; i++) | |
{ | |
await GetData(); | |
} | |
Console.WriteLine($"{stayedOnContext}/{n}"); | |
} | |
public async Task<string> GetData() => await GetA().ConfigureAwait(false) + await GetB().ConfigureAwait(false); | |
private async Task<string> GetA() => await GetFile("a.txt").ConfigureAwait(false); | |
private async Task<string> GetB() | |
{ | |
var before = SynchronizationContext.Current != null; | |
var result = await GetFile("b.txt").ConfigureAwait(false); | |
var after = SynchronizationContext.Current != null; | |
if (before && !after) | |
stayedOnContext++; | |
return result; | |
} | |
private async Task<string> GetFile(string name) | |
{ | |
using (var reader = new StreamReader(name)) | |
{ | |
return await reader.ReadToEndAsync().ConfigureAwait(false); | |
} | |
} | |
} | |
// https://stackoverflow.com/a/31714115/41071 | |
public sealed class DedicatedThreadSynchronisationContext : SynchronizationContext, IDisposable | |
{ | |
public DedicatedThreadSynchronisationContext() | |
{ | |
m_thread = new Thread(ThreadWorkerDelegate); | |
m_thread.Start(this); | |
} | |
public void Dispose() | |
{ | |
m_queue.CompleteAdding(); | |
} | |
/// <summary>Dispatches an asynchronous message to the synchronization context.</summary> | |
/// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param> | |
/// <param name="state">The object passed to the delegate.</param> | |
public override void Post(SendOrPostCallback d, object state) | |
{ | |
if (d == null) | |
throw new ArgumentNullException("d"); | |
m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state)); | |
} | |
/// <summary> As | |
public override void Send(SendOrPostCallback d, object state) | |
{ | |
using (var handledEvent = new ManualResetEvent(false)) | |
{ | |
Post(SendOrPostCallback_BlockingWrapper, Tuple.Create(d, state, handledEvent)); | |
handledEvent.WaitOne(); | |
} | |
} | |
public int WorkerThreadId | |
{ | |
get { return m_thread.ManagedThreadId; } | |
} | |
//========================================================================================= | |
private static void SendOrPostCallback_BlockingWrapper(object state) | |
{ | |
var innerCallback = (state as Tuple<SendOrPostCallback, object, ManualResetEvent>); | |
try | |
{ | |
innerCallback.Item1(innerCallback.Item2); | |
} | |
finally | |
{ | |
innerCallback.Item3.Set(); | |
} | |
} | |
/// <summary>The queue of work items.</summary> | |
private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue = | |
new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); | |
private readonly Thread m_thread = null; | |
/// <summary>Runs an loop to process all queued work items.</summary> | |
private void ThreadWorkerDelegate(object obj) | |
{ | |
SynchronizationContext.SetSynchronizationContext(obj as SynchronizationContext); | |
try | |
{ | |
foreach (var workItem in m_queue.GetConsumingEnumerable()) | |
workItem.Key(workItem.Value); | |
} | |
catch (ObjectDisposedException) | |
{ | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment