Skip to content

Instantly share code, notes, and snippets.

@NtFreX
Last active May 19, 2020 15:40
Show Gist options
  • Save NtFreX/a908ce8a530b65ec4e08f01bd8ed5ab1 to your computer and use it in GitHub Desktop.
Save NtFreX/a908ce8a530b65ec4e08f01bd8ed5ab1 to your computer and use it in GitHub Desktop.
public class SingleStaTaskScheduler : TaskScheduler, IDisposable
{
private readonly BlockingCollection<Task> _taskQueue = new BlockingCollection<Task>();
private readonly CancellationTokenSource _cancellationTokenSource;
private readonly object _lock = new object();
private readonly EventWaitHandle _eventWaitHandle;
private readonly Thread _staThread;
public SingleStaTaskScheduler()
{
_cancellationTokenSource = new CancellationTokenSource();
_eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
_staThread = new Thread(ThreadWorker);
_staThread.SetApartmentState(ApartmentState.STA);
_staThread.Name = nameof(SingleStaTaskScheduler);
_staThread.Start();
}
private void NotifyWorkerAboutPendingTask()
=> _eventWaitHandle.Set();
private void ThreadWorker()
{
while (!Monitor.TryEnter(_lock))
Monitor.Wait(_lock);
while (!_cancellationTokenSource.IsCancellationRequested)
{
if (_taskQueue.TryTake(out Task task))
{
TryExecuteTask(task);
}
else
{
_eventWaitHandle.WaitOne();
}
}
Monitor.Exit(_lock);
}
protected override void QueueTask(Task task)
{
_taskQueue.Add(task);
NotifyWorkerAboutPendingTask();
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
=> Thread.CurrentThread.GetApartmentState() == ApartmentState.STA && Thread.CurrentThread.ManagedThreadId == _staThread.ManagedThreadId && TryExecuteTask(task);
protected override IEnumerable<Task> GetScheduledTasks()
=> _taskQueue.ToList();
public void Dispose()
{
_cancellationTokenSource.Cancel();
while (!Monitor.TryEnter(_lock))
Monitor.Wait(_lock);
_staThread.Abort();
Monitor.Exit(_lock);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment