Make the TaskScheduler synchronously and run in the main thread - c#

I'm looking for a way to create a TaskScheduler that runs synchronously in the main thread to allow WPF applications to be configured as single thread for debugging purpose.
Any idea?
For now I'm using the sample LimitedTaskScheduler on MSDN that allow to specify the concurrency level (how many threads use) and this extension to set the static TaskFactory before the application starts:
void SetOnTaskFactory(TaskFactory taskFactory)
{
const BindingFlag = BindingFlags.Static | BindingFlags.NonPublic
var field = typeof(Task).GetField("s_factory", BindingFlag);
field.SetValue(null, taskFactory);
}

For testing purposes you can use the CurrentThreadTaskScheduler from ParallelExtensionsExtras library.
Basically it's a simple TaskScheduler that executes all tasks on the current thread.

If you want to create a SynchronousTaskScheduler, you can do so using below code.
void Main()
{
SynchronousTaskScheduler taskScheduler = new SynchronousTaskScheduler();
for (int i = 0; i < 100; i++)
{
Task.Factory.StartNew(() => SomeMethod(i), CancellationToken.None, TaskCreationOptions.None, taskScheduler);
}
}
void SomeMethod(int number)
{
$"Scheduled task {number}".Dump();
}
// Define other methods and classes here
class SynchronousTaskScheduler : TaskScheduler
{
public override int MaximumConcurrencyLevel
{
get { return 1; }
}
protected override void QueueTask(Task task)
{
TryExecuteTask(task);
}
protected override bool TryExecuteTaskInline(
Task task,
bool taskWasPreviouslyQueued)
{
return TryExecuteTask(task);
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return Enumerable.Empty<Task>();
}
}

Related

Task counter C#, Interlocked

I would like to run tasks in parallel, with no more than 10 instances running at a given time.
This is the code I have so far:
private void Listen()
{
while (true)
{
var context = listener.GetContext();
var task = Task.Run(() => HandleContextAsync(context));
Interlocked.Increment(ref countTask);
if (countTask > 10)
{
//I save tasks in the collection
}
else
{
task.ContinueWith(delegate { Interlocked.Decrement(ref countTask); }); //I accomplish the task and reduce the counter
}
}
}
I would suggest that you use a Parallel loop; for example:
Parallel.For(1, 10, a =>
{
var context = listener.GetContext();
...
});
That will start a defined number of tasks without you needing to manage the process yourself.
If you want to continually execute code in parallel, with up to 10 instances at a time, this may be worth considering:
private void Listen()
{
var options = new ParallelOptions() { MaxDegreeOfParallelism = 10 };
Parallel.For(1, long.MaxValue - 1, options, (i) =>
{
var context = listener.GetContext();
HandleContextAsync(context);
});
}
Basically, it will run the code continually (well roughly long.MaxValue times). MaxDegreeOfParallelism ensures that it runs only 10 'instances' of the code at a time.
I'm assuming that the result from GetContext is not created by you, so, its probably not useful to use a Parallel.For when you don't know how many times to run or don't have all the contexts to handle right away.
So, probably the best way to resolve this would be by implementing your own TaskScheduler. This way you can add more tasks to be resolved on demand with a fixed concurrency level.
Based on the example from Microsoft Docs website you can already achieve this.
I made an example program with some changes to the LimitedConcurrencyLevelTaskScheduler from the website.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace parallel
{
class Program
{
private static Random Rand = new Random();
static void Main(string[] args)
{
var ts = new LimitedConcurrencyLevelTaskScheduler(10);
var taskFactory = new TaskFactory(ts);
while (true)
{
var context = GetContext(ts);
if (context.Equals("Q", StringComparison.OrdinalIgnoreCase))
break;
taskFactory.StartNew(() => HandleContextAsync(context));
}
Console.WriteLine("Waiting...");
while (ts.CountRunning != 0)
{
Console.WriteLine("Now running {0}x tasks with {1}x queued.", ts.CountRunning, ts.CountQueued);
Thread.Yield();
Thread.Sleep(100);
}
}
private static void HandleContextAsync(string context)
{
// delays for 1-10 seconds to make the example easier to understand
Thread.Sleep(Rand.Next(1000, 10000));
Console.WriteLine("Context: {0}, from thread: {1}", context, Thread.CurrentThread.ManagedThreadId);
}
private static string GetContext(LimitedConcurrencyLevelTaskScheduler ts)
{
Console.WriteLine("Now running {0}x tasks with {1}x queued.", ts.CountRunning, ts.CountQueued);
return Console.ReadLine();
}
}
// Provides a task scheduler that ensures a maximum concurrency level while
// running on top of the thread pool.
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
{
// Indicates whether the current thread is processing work items.
[ThreadStatic]
private static bool _currentThreadIsProcessingItems;
// The list of tasks to be executed
private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); // protected by lock(_tasks)
public int CountRunning => _nowRunning;
public int CountQueued
{
get
{
lock (_tasks)
{
return _tasks.Count;
}
}
}
// The maximum concurrency level allowed by this scheduler.
private readonly int _maxDegreeOfParallelism;
// Indicates whether the scheduler is currently processing work items.
private volatile int _delegatesQueuedOrRunning = 0;
private volatile int _nowRunning;
// Creates a new instance with the specified degree of parallelism.
public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism)
{
if (maxDegreeOfParallelism < 1)
throw new ArgumentOutOfRangeException("maxDegreeOfParallelism");
_maxDegreeOfParallelism = maxDegreeOfParallelism;
}
// Queues a task to the scheduler.
protected sealed override void QueueTask(Task task)
{
// Add the task to the list of tasks to be processed. If there aren't enough
// delegates currently queued or running to process tasks, schedule another.
lock (_tasks)
{
_tasks.AddLast(task);
if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism)
{
Interlocked.Increment(ref _delegatesQueuedOrRunning);
NotifyThreadPoolOfPendingWork();
}
}
}
// Inform the ThreadPool that there's work to be executed for this scheduler.
private void NotifyThreadPoolOfPendingWork()
{
ThreadPool.UnsafeQueueUserWorkItem(_ =>
{
// Note that the current thread is now processing work items.
// This is necessary to enable inlining of tasks into this thread.
_currentThreadIsProcessingItems = true;
try
{
// Process all available items in the queue.
while (true)
{
Task item;
lock (_tasks)
{
// When there are no more items to be processed,
// note that we're done processing, and get out.
if (_tasks.Count == 0)
{
Interlocked.Decrement(ref _delegatesQueuedOrRunning);
break;
}
// Get the next item from the queue
item = _tasks.First.Value;
_tasks.RemoveFirst();
}
// Execute the task we pulled out of the queue
Interlocked.Increment(ref _nowRunning);
if (base.TryExecuteTask(item))
Interlocked.Decrement(ref _nowRunning);
}
}
// We're done processing items on the current thread
finally { _currentThreadIsProcessingItems = false; }
}, null);
}
// Attempts to execute the specified task on the current thread.
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// If this thread isn't already processing a task, we don't support inlining
if (!_currentThreadIsProcessingItems) return false;
// If the task was previously queued, remove it from the queue
if (taskWasPreviouslyQueued)
// Try to run the task.
if (TryDequeue(task))
return base.TryExecuteTask(task);
else
return false;
else
return base.TryExecuteTask(task);
}
// Attempt to remove a previously scheduled task from the scheduler.
protected sealed override bool TryDequeue(Task task)
{
lock (_tasks) return _tasks.Remove(task);
}
// Gets the maximum concurrency level supported by this scheduler.
public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } }
// Gets an enumerable of the tasks currently scheduled on this scheduler.
protected sealed override IEnumerable<Task> GetScheduledTasks()
{
bool lockTaken = false;
try
{
Monitor.TryEnter(_tasks, ref lockTaken);
if (lockTaken) return _tasks;
else throw new NotSupportedException();
}
finally
{
if (lockTaken) Monitor.Exit(_tasks);
}
}
}
}

.NET TaskScheduler, async/await, ensuring that Tasks never run longer than a certain period of time

I am writing a WPF project, using C# 5 and async/await.
I'd like to, during development, add some code that will alert the developer any task takes longer than a certain period of time. This will ensure that the developer never accidentally does file/network IO on the UI thread, as well as any other long running computations that should be moved to another thread.
Is there somewhere to override the TaskScheduler, to wrap each Task with the following?
private Task WrapTask(Task task)
{
return Task.Run(async () =>
{
var stopwatch = new Stopwatch();
stopwatch.Start();
await task;
stopwatch.Stop();
if (stopwatch.Elapsed > TimeSpan.FromMilliseconds(5))
{
// TODO: Log
Debug.WriteLine("A task took longer than expected.");
}
});
}
This should be transparent to the user, and also should be used when in the context of an async/await method.
THIS DOESN'T WORK AT ALL, JUST TO ILLUSTRATE: Maybe wrapping a TaskScheduler like this, and then someone replacing the current one?
public class TaskSchedulerTimer : TaskScheduler
{
private readonly TaskScheduler _taskScheduler;
private readonly MethodInfo _queueTaskMethod;
private readonly MethodInfo _tryExecuteTaskInlineMethod;
private readonly MethodInfo _getScheduledTasksMethod;
public TaskSchedulerTimer(TaskScheduler taskScheduler)
{
_taskScheduler = taskScheduler;
_queueTaskMethod = typeof(TaskScheduler).GetMethod("QueueTask");
_tryExecuteTaskInlineMethod = typeof(TaskScheduler).GetMethod("TryExecuteTaskInline");
_getScheduledTasksMethod = typeof(TaskScheduler).GetMethod("GetScheduledTasks");
}
protected override void QueueTask(Task task)
{
_queueTaskMethod.Invoke(_taskScheduler, new object[] { WrapTask(task) });
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
return (bool)_tryExecuteTaskInlineMethod.Invoke(_taskScheduler, new object[] { WrapTask(task), taskWasPreviouslyQueued });
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return (IEnumerable<Task>)_getScheduledTasksMethod.Invoke(_taskScheduler, new object[] { });
}
private Task WrapTask(Task task)
{
return Task.Run(async () =>
{
var stopwatch = new Stopwatch();
stopwatch.Start();
await task;
stopwatch.Stop();
if (stopwatch.Elapsed > TimeSpan.FromMilliseconds(5))
{
// TODO: Log
Debug.WriteLine("A task took longer than expected.");
}
});
}
}
Maybe I need to go lower, to the SynchronizationContext, and do something similar there?
UPDATE: It seems that the current TaskScheduler used in WPF wraps around the Dispatcher. There appears to be some hooks on there, so I am covered for my purposes. However, I'd still like to know if my original question has a good answer.
FYI, here is my code for the timing stuff, in WPF.
private readonly Stopwatch _currentOperation = new Stopwatch();
Dispatcher.Hooks.OperationStarted += HooksOnOperationStarted;
Dispatcher.Hooks.OperationCompleted += HooksOnOperationCompleted;
Dispatcher.Hooks.OperationAborted += HooksOnOperationAborted;
private void HooksOnOperationStarted(object sender, DispatcherHookEventArgs dispatcherHookEventArgs)
{
Debug.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId == Dispatcher.Thread.ManagedThreadId);
_currentOperation.Start();
}
private void HooksOnOperationCompleted(object sender, DispatcherHookEventArgs dispatcherHookEventArgs)
{
Debug.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId == Dispatcher.Thread.ManagedThreadId);
_currentOperation.Stop();
if (_currentOperation.Elapsed > TimeSpan.FromMilliseconds(5))
{
// TODO: Log
Debug.WriteLine("A task took longer than expected.");
}
_currentOperation.Reset();
}
private void HooksOnOperationAborted(object sender, DispatcherHookEventArgs dispatcherHookEventArgs)
{
Debug.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId == Dispatcher.Thread.ManagedThreadId);
_currentOperation.Stop();
if (_currentOperation.Elapsed > TimeSpan.FromMilliseconds(5))
{
// TODO: Log
Debug.WriteLine("A task took longer than expected.");
}
_currentOperation.Reset();
}

How to use ActiveX component in ClassLibrary without Winforms

How is it possible to use an ActiveX control in a ClassLibrary type project?
I intend to call it later from WPF application but I don't want to place a control anywhere on the form, so I don't want to use WindowsFormsHost; mainly because I would like to use this my library in Console App and Windows Service.
In this case, the ActiveX control I want to use is a video analysis component.
Additionally I want my component to register itself in deployed environment.
I think that the common knowledge is that you need Winforms to be able to use ActiveX control. Well, not entirely true. You need winforms-like message loop and STAThread.
Let's start by presenting the design of my solution. I prefer to seperate code to as many layers as needed when dealing with something unknown so you may find some layers redundant. I encourage you to help me improve the solution to find the equilibrium.
Please remember about the need to implement the IDisposable interface in all outer layers if needed.
ActiveXCore - class containing an ActiveX control declared as a private field. In this class you use just code like you would in Winforms.
CoreAPI - an internal API class that exposes the methods of ActiveXCore. I found out that it is good to mark the methods with [STAThreadAttribute] as I had some problems without it, though it may be specific to this case only.
PublicAPI - my main library class that will be called in the referencing projects.
Now, in the ActiveXCore there are really no guidelines.
In CoreAPI the sample method would be
[STAThreadAttribute]
internal bool Init()
{
try
{
_core = new ActiveXCore();
//...
return true;
}
catch (System.Runtime.InteropServices.COMException)
{
//handle the exception
}
return false;
}
To be able to properly run these you would need Winforms like message loop like this (the desing is not mine at all, I just slightly modified the code). You don't need the Winforms project type, but you have to reference System.Windows.Forms assembly
public class MessageLoopApartment : IDisposable
{
public static MessageLoopApartment Apartament
{
get
{
if (_apartament == null)
_apartament = new MessageLoopApartment();
return _apartament;
}
}
private static MessageLoopApartment _apartament;
private Thread _thread; // the STA thread
private TaskScheduler _taskScheduler; // the STA thread's task scheduler
public TaskScheduler TaskScheduler { get { return _taskScheduler; } }
/// <summary>MessageLoopApartment constructor</summary>
public MessageLoopApartment()
{
var tcs = new TaskCompletionSource<TaskScheduler>();
// start an STA thread and gets a task scheduler
_thread = new Thread(startArg =>
{
EventHandler idleHandler = null;
idleHandler = (s, e) =>
{
// handle Application.Idle just once
Application.Idle -= idleHandler;
// return the task scheduler
tcs.SetResult(TaskScheduler.FromCurrentSynchronizationContext());
};
// handle Application.Idle just once
// to make sure we're inside the message loop
// and SynchronizationContext has been correctly installed
Application.Idle += idleHandler;
Application.Run();
});
_thread.SetApartmentState(ApartmentState.STA);
_thread.IsBackground = true;
_thread.Start();
_taskScheduler = tcs.Task.Result;
}
/// <summary>shutdown the STA thread</summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_taskScheduler != null)
{
var taskScheduler = _taskScheduler;
_taskScheduler = null;
// execute Application.ExitThread() on the STA thread
Task.Factory.StartNew(
() => Application.ExitThread(),
CancellationToken.None,
TaskCreationOptions.None,
taskScheduler).Wait();
_thread.Join();
_thread = null;
}
}
/// <summary>Task.Factory.StartNew wrappers</summary>
public void Invoke(Action action)
{
Task.Factory.StartNew(action,
CancellationToken.None, TaskCreationOptions.None, _taskScheduler).Wait();
}
public TResult Invoke<TResult>(Func<TResult> action)
{
return Task.Factory.StartNew(action,
CancellationToken.None, TaskCreationOptions.None, _taskScheduler).Result;
}
public Task Run(Action action, CancellationToken token)
{
return Task.Factory.StartNew(action, token, TaskCreationOptions.None, _taskScheduler);
}
public Task<TResult> Run<TResult>(Func<TResult> action, CancellationToken token)
{
return Task.Factory.StartNew(action, token, TaskCreationOptions.None, _taskScheduler);
}
public Task Run(Func<Task> action, CancellationToken token)
{
return Task.Factory.StartNew(action, token, TaskCreationOptions.None, _taskScheduler).Unwrap();
}
public Task<TResult> Run<TResult>(Func<Task<TResult>> action, CancellationToken token)
{
return Task.Factory.StartNew(action, token, TaskCreationOptions.None, _taskScheduler).Unwrap();
}
}
And then you can provide methods like that
public bool InitLib()
{
return MessageLoopApartment.Apartament.Run(() =>
{
ca = new CoreAPI();
bool initialized = ca.Init();
}, CancellationToken.None).Result;
}
of if the method would be void
public void InitLib()
{
MessageLoopApartment.Apartament.Run(() =>
{
ca = new CoreAPI();
ca.Init();
}, CancellationToken.None).Wait();
}
As for the auto registering I designed something like this (I call it from CoreAPI)
internal static class ComponentEnvironment
{
internal static void Prepare()
{
CopyFilesAndDeps();
if (Environment.Is64BitOperatingSystem)
RegSvr64();
RegSvr32(); //you may notice no "else" here
//in my case for 64 bit I had to copy and register files for both arch
}
#region unpack and clean files
private static void CopyFilesAndDeps()
{
//inspect what file you need
}
#endregion unpack and clean files
#region register components
private static void RegSvr32()
{
string dllPath = #"xxx\x86\xxx.dll";
Process.Start("regsvr32", "/s " + dllPath);
}
private static void RegSvr64()
{
string dllPath = #"xxx\x64\xxx.dll";
Process.Start("regsvr32", "/s " + dllPath);
}
#endregion register components
}
I spent many days and nights to design this reusable pattern so I hope it will help someone.

Thread Join() causes Task.RunSynchronously not to finish

Calling _thread.Join() causes the GetConsumingEnumerable loop to be stuck on the last element. Why does this behavior occur?
public abstract class ActorBase : IDisposable
{
private readonly BlockingCollection<Task> _queue = new BlockingCollection<Task>(new ConcurrentQueue<Task>());
private readonly Thread _thread;
private bool _isDisposed;
protected ActorBase()
{
_thread = new Thread(ProcessMessages);
_thread.Start();
}
protected void QueueTask(Task task)
{
if (_isDisposed)
{
throw new Exception("Actor was disposed, cannot queue task.");
}
_queue.Add(task);
}
private void ProcessMessages()
{
foreach (var task in _queue.GetConsumingEnumerable())
{
task.RunSynchronously();
}
}
public void Dispose()
{
_isDisposed = true;
_queue.CompleteAdding();
_thread.Join();
}
}
public class SampleActor : ActorBase
{
private string GetThreadStatus()
{
Thread.Sleep(500);
return string.Format("Running on thread {0}", Thread.CurrentThread.ManagedThreadId);
}
public async Task<string> GetThreadStatusAsync()
{
var task = new Task<string>(GetThreadStatus);
QueueTask(task);
return await task;
}
}
class Program
{
public static async Task Run()
{
using (var sa = new SampleActor())
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine(await sa.GetThreadStatusAsync());
}
}
}
public static void Main(string[] args)
{
Console.WriteLine("Main thread id {0}", Thread.CurrentThread.ManagedThreadId);
var task = Task.Run(async ()=> { await Run(); });
task.Wait();
}
}
The context for this approach is that I need to make sure that all operations are executed on one OS thread, which would allow a part of the app to use different credentials than the main thread.
async-await works with continuations. To be efficient and reduce scheduling these continuations usually run on the same thread that completed the previous task.
That means in your case that your special thread is not only running the tasks, it's also running all the continuations after these tasks (the for loop itself). You can see that by printing the thread id:
using (var sa = new SampleActor())
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine(await sa.GetThreadStatusAsync());
Console.WriteLine("Continue on thread :" + Thread.CurrentThread.ManagedThreadId);
}
}
When the for loop completes and the SampleActor is being disposed you call Thread.Join from the same thread your are trying to join so you get a deadlock. Your situation boils down to this:
public static void Main()
{
Thread thread = null;
thread = new Thread(() =>
{
Thread.Sleep(100);
thread.Join();
Console.WriteLine("joined");
});
thread.Start();
}
In .Net 4.6 you can solve this with TaskCreationOptions.RunContinuationsAsynchronously but in the current version you can specify the default TaskScheduler:
public Task<string> GetThreadStatusAsync()
{
var task = new Task<string>(GetThreadStatus);
QueueTask(task);
return task.ContinueWith(task1 => task1.GetAwaiter().GetResult(), TaskScheduler.Default);
}
It might be tempting to put a simple check to see if the thread you're trying to Join is Thread.CurrentThread, but that would be wrong.
Furthermore, I think the whole approach - scheduling and running cold Task objects with a custom, non-TPL-compliant scheduler - is wrong. You should be using a TPL-friendly task scheduler, similar to Stephen Toub's StaTaskScheduler. Or run a custom SynchronizationContext for your actor-serving thread (like Toub's AsyncPump) and use TaskScheduler.FromCurrentSynchronizationContext and Task.Factory.StartNew to schedue tasks with your custom scheduler (or use Task.Start(TaskScheduler) if you have to deal with cold tasks).
This way, you'll have full control of where tasks and their continuations run, as well as of task inlining.

How to get current running task in a blockingqueue of C#?

I want to get the current executing Task that I am adding in the blockingqueue, how it can be done?
EDIT: I am using this priority scheduler, and adding multiple tasks with different priorities:
public class PriorityScheduler : TaskScheduler
{
public static PriorityScheduler Highest = new PriorityScheduler(ThreadPriority.Highest);
public static PriorityScheduler AboveNormal = new PriorityScheduler(ThreadPriority.AboveNormal);
public static PriorityScheduler Normal = new PriorityScheduler(ThreadPriority.Normal);
public static PriorityScheduler BelowNormal = new PriorityScheduler(ThreadPriority.BelowNormal);
public static PriorityScheduler Lowest = new PriorityScheduler(ThreadPriority.Lowest);
public static BlockingCollection<Task> _tasks = new BlockingCollection<Task>();
private Thread[] _threads;
private ThreadPriority _priority;
private readonly int _maximumConcurrencyLevel = Math.Max(1, Environment.ProcessorCount);
public PriorityScheduler(ThreadPriority priority)
{
_priority = priority;
}
public override int MaximumConcurrencyLevel
{
get { return _maximumConcurrencyLevel; }
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return _tasks;
}
protected override void QueueTask(Task task)
{
_tasks.Add(task);
if (_threads == null)
{
_threads = new Thread[_maximumConcurrencyLevel];
for (int i = 0; i < _threads.Length; i++)
{
int local = i;
_threads[i] = new Thread(() =>
{
foreach (Task t in _tasks.GetConsumingEnumerable())
base.TryExecuteTask(t);
});
_threads[i].Name = string.Format("PriorityScheduler: ", i);
_threads[i].Priority = _priority;
_threads[i].IsBackground = true;
_threads[i].Start();
}
}
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
return false; // we might not want to execute task that should schedule as high or low priority inline
}
}
I want to stop and resume tasks depending upon there priorities, e.g. if a new task with higher priority arrives, the lower stops and lets the task execute and then resumes itself...
If you are referring to BlockingCollection<T>, you can't (directly). When you call Take() (or get the next item via GetConsumingEnumerable()), the item (task?) is actually removed from the underlying connection.
You would need to have your consumer store and expose the "current task" if you want this to be available.
Note that the Parallel Extension Extras project provides a great QueuedTaskScheduler which may accomplish your goals here. It allows you to create prioritized TaskScheduler instances, and handles all of the scheduling for you.

Categories

Resources