Skip to content

Instantly share code, notes, and snippets.

@svick
Created June 2, 2017 14:13
Show Gist options
  • Save svick/7307338f950ef972cbf4d57efad81713 to your computer and use it in GitHub Desktop.
Save svick/7307338f950ef972cbf4d57efad81713 to your computer and use it in GitHub Desktop.
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