using System.Collections.Generic;
using System.Threading.Tasks;
public static async Task Main() {
var scheduler = new SerialScheduler();
PrintSchedulerStatus("Outside");
await Task.Factory.StartNew(async () => {
PrintSchedulerStatus("Parent");
await Task.Run(() => PrintSchedulerStatus("Child"))
Console.WriteLine("continueOnCapturedContext=true");
PrintSchedulerStatus("Parent");
await Task.Factory.StartNew(() => PrintSchedulerStatus("Child"))
Console.WriteLine("continueOnCapturedContext=false");
PrintSchedulerStatus("Parent");
await Task.Factory.StartNew(() => PrintSchedulerStatus("Child"), default, default, scheduler)
Console.WriteLine("continueOnCapturedContext=false");
PrintSchedulerStatus("Parent");
}, default, default, scheduler).Unwrap();
PrintSchedulerStatus("Outside");
static void PrintSchedulerStatus(string label) {
Console.WriteLine(" {0,7} {1}", label, TaskScheduler.Current == TaskScheduler.Default ? "Default" : "Custom");
public class SerialScheduler : TaskScheduler {
private static bool _currentThreadIsProcessingItems;
private readonly LinkedList<Task> _tasks = new LinkedList<Task>();
protected sealed override void QueueTask(Task task) {
NotifyThreadPoolOfPendingWork();
private void NotifyThreadPoolOfPendingWork() {
ThreadPool.UnsafeQueueUserWorkItem(_ => {
_currentThreadIsProcessingItems = true;
item = _tasks.First.Value;
base.TryExecuteTask(item);
_currentThreadIsProcessingItems = false;
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) {
if (!_currentThreadIsProcessingItems) {
if (taskWasPreviouslyQueued) {
return TryDequeue(task) && base.TryExecuteTask(task);
return base.TryExecuteTask(task);
protected sealed override bool TryDequeue(Task task) {
return _tasks.Remove(task);
protected sealed override IEnumerable<Task> GetScheduledTasks() {
Monitor.TryEnter(_tasks, ref lockTaken);
throw new NotSupportedException();