Serial Task Executor; is this thread safe? - c#

I have a class that I've created to allow asynchronous sequential execution of tasks, using the ThreadPool as the means of execution. The idea is that I'll have multiple instances running serial tasks in the background, but I don't want to have a separate dedicated Thread for each instance. What I'd like to check is whether this class is actually thread safe. It's fairly brief, so I thought I'd run it by the experts here, in case I'm missing something obvious. I've omitted a few of the convenience overloads for different Action types.
/// <summary>
/// This class wraps ThreadPool.QueueUserWorkItem, but providing guaranteed ordering of queued tasks for this instance.
/// Only one task in the queue will execute at a time, with the order of execution matching the order of addition.
/// This is designed as a lighter-weight alternative to using a dedicated Thread for processing of sequential tasks.
/// </summary>
public sealed class SerialAsyncTasker
{
private readonly Queue<Action> mTasks = new Queue<Action>();
private bool mTaskExecuting;
/// <summary>
/// Queue a new task for asynchronous execution on the thread pool.
/// </summary>
/// <param name="task">Task to execute</param>
public void QueueTask(Action task)
{
if (task == null) throw new ArgumentNullException("task");
lock (mTasks)
{
bool isFirstTask = (mTasks.Count == 0);
mTasks.Enqueue(task);
//Only start executing the task if this is the first task
//Additional tasks will be executed normally as part of sequencing
if (isFirstTask && !mTaskExecuting)
RunNextTask();
}
}
/// <summary>
/// Clear all queued tasks. Any task currently executing will continue to execute.
/// </summary>
public void Clear()
{
lock (mTasks)
{
mTasks.Clear();
}
}
/// <summary>
/// Wait until all currently queued tasks have completed executing.
/// If no tasks are queued, this method will return immediately.
/// This method does not prevent the race condition of a second thread
/// queueing a task while one thread is entering the wait;
/// if this is required, it must be synchronized externally.
/// </summary>
public void WaitUntilAllComplete()
{
lock (mTasks)
{
while (mTasks.Count > 0 || mTaskExecuting)
Monitor.Wait(mTasks);
}
}
private void RunTask(Object state)
{
var task = (Action)state;
task();
mTaskExecuting = false;
RunNextTask();
}
private void RunNextTask()
{
lock (mTasks)
{
if (mTasks.Count > 0)
{
mTaskExecuting = true;
var task = mTasks.Dequeue();
ThreadPool.QueueUserWorkItem(RunTask, task);
}
else
{
//If anybody is waiting for tasks to be complete, let them know
Monitor.PulseAll(mTasks);
}
}
}
}
UPDATE: I've revised the code to fix the main bugs kindly pointed out by Simon. This passes unit tests now, but I still welcome observations.

Don't do it. (Or at least avoid building your own stuff.)
Use the System.Threading.Tasks stuff (new in .NET 4.0). Create your a Task[] (size depends on number of parallel tasks you want) and let them read work items from a BlockingCollection while waiting for a CancellationToken. Your WaitForAll implementation would trigger your token, and call Task.WaitAll(Task[]) which will block until all your tasks are done.

Here's my second answer assuming that you cant use .NET 4.0 (and want comments on your existing code).
QueueTask enqueues the first task, getting isFirstTask = true, and starts a new thread. However, another thread may enqueue something while the first thread is processing, and Count == 0 => isFirstTask = true, and yet another thread is spawned.
Also, WaitUntilAllComplete will hang indefinitely if the task execution throws an exception (which may not necessarily crash everything, depending on exception handling), causing it to skip the call to RunNextTask().
And your WaitUntilAllComplete just waits until there are no more enqueue tasks, not that those currently executing are actually executing (they could just be enqueued in the ThreadPool) or complete.

It's built in in 4.0
How to: Create a Task Scheduler That Limits the Degree of Concurrency
You can also use a custom scheduler to achieve functionality that the default scheduler does not provide, such as strict first-in, first-out (FIFO) execution order. The following example demonstrates how to create a custom task scheduler. This scheduler lets you specify the degree of concurrency.

I see a few issues your with your SerialAsyncTasker class, but it sounds like you might have a good grasp of those so I will not go into any details on that topic (I may edit my answer with more details later). You indicated in the comments that you cannot use .NET 4.0 features nor can you use the Reactive Extensions backport. I propose that you use the producer-consumer pattern with a single consumer on a dedicated thread. This would perfectly fit your requirement of asynchronously executing tasks sequentially.
Note: You will have to harden the code to support gracefully shutting down, handling exceptions, etc.
public class SerialAsyncTasker
{
private BlockingCollection<Action> m_Queue = new BlockingCollection<Action>();
public SerialAsyncTasker()
{
var thread = new Thread(
() =>
{
while (true)
{
Action task = m_Queue.Take();
task();
}
});
thread.IsBackground = true;
thread.Start();
}
public void QueueTask(Action task)
{
m_Queue.Add(task);
}
}
Too bad you cannot use the BlockingCollection from the .NET 4.0 BCL or Reactive Extension download, but no worries. It is actually not too hard to implement one yourself. You can use Stephen Toub's blocking queue as a starting point and just rename a few things.
public class BlockingCollection<T>
{
private Queue<T> m_Queue = new Queue<T>();
public T Take()
{
lock (m_Queue)
{
while (m_Queue.Count <= 0) Monitor.Wait(m_Queue);
return m_Queue.Dequeue();
}
}
public void Add(T value)
{
lock (m_Queue)
{
m_Queue.Enqueue(value);
Monitor.Pulse(m_Queue);
}
}
}

public class ParallelExcecuter
{
private readonly BlockingCollection<Task> _workItemHolder;
public ParallelExcecuter(int maxDegreeOfParallelism)
{
_workItemHolder = new BlockingCollection<Task>(maxDegreeOfParallelism);
}
public void Submit(Action action)
{
_workItemHolder.Add(Task.Run(action).ContinueWith(t =>
{
_workItemHolder.Take();
}));
}
public void WaitUntilWorkDone()
{
while (_workItemHolder.Count < 0)
{
Monitor.Wait(_workItemHolder);
}
}
}

Related

Why is Task.Delay breaking the STA state of the thread?

Introduction
This is a lengthy question! You will find some background on the problem at the beginning, then code samples, which have been simplified for representation and the Question after that. Please read in any order that you find good for you!
Background information
I am writing a Proof-of-Concept part for an application for communicating with an STA COM. This part of the application has the requirement of running in a Single-Threaded Apartment (STA) context in order to communicate with said STA COM. The rest of the application runs in a MTA context.
Current state
What I have come up with so far is creating a Communication class that contains a while loop, running in a STA. The work that needs to be relayed to the COM object is queued from the outside to the Communication class via ConcurrentQueue. The work items are then dequeued in the while loop and the work is performed.
Code context
Communication class
This is a static class, containing a loop that is intended to run in STA state and check if some work needs to be done by the COM and dispatch the work to the handler.
static class Communication
{
#region Public Events
/// This event is raised when the COM object has been initialized
public static event EventHandler OnCOMInitialized;
#endregion Public Events
#region Private Members
/// Stores a reference to the COM object
private static COMType s_comObject;
/// Used to queue work that needs to be done by the COM object
private static ConcurrentQueue<WorkUnit> s_workQueue;
#endregion Private Members
#region Private Methods
/// Initializes the COM object
private static void InternalInitializeCOM()
{
s_comObject = new COMType();
if (s_comObject.Init())
{
OnCOMInitialized?.Invoke(null, EventArgs.Empty);
}
}
/// Dispatches the work unit to the correct handler
private static void HandleWork(WorkUnit work)
{
switch (work.Command)
{
case WorkCommand.Initialize:
InternalInitializeCOM();
break;
default:
break;
}
}
#endregion Private Methods
#region Public Methods
/// Starts the processing loop
public static void StartCommunication()
{
s_workQueue = new ConcurrentQueue<WorkUnit>();
while (true)
{
if (s_workQueue.TryDequeue(out var workUnit))
{
HandleWork(workUnit);
}
// [Place for a delaying logic]
}
}
/// Wraps the work unit creation for the task of Initializing the COM
public static void InitializeCOM()
{
var workUnit = new WorkUnit(
command: WorkCommand.Initialize,
arguments: null
);
s_workQueue.Enqueue(workUnit);
}
#endregion Public Methods
}
Work command
This class describes the work that needs to be done and any arguments that might be provided.
enum WorkCommand
{
Initialize
}
Work unit
This enumeration defines the various tasks that can be performed by the COM.
class WorkUnit
{
#region Public Properties
public WorkCommand Command { get; private set; }
public object[] Arguments { get; private set; }
#endregion Public Properties
#region Constructor
public WorkUnit(WorkCommand command, object[] arguments)
{
Command = command;
Arguments = arguments == null
? new object[0]
: arguments;
}
#endregion Constructor
}
Owner
This is a sample of the class that owns or spawns the Communication with the COM and is an abstraction over the Communication for use in the rest of the application.
class COMController
{
#region Public Events
/// This event is raised when the COM object has been initialized
public event EventHandler OnInitialize;
#endregion Public Events
#region Constructor
/// Creates a new COMController instance and starts the communication
public COMController()
{
var communicationThread = new Thread(() =>
{
Communication.StartCommunication();
});
communicationThread.SetApartmentState(ApartmentState.STA);
communicationThread.Start();
Communication.OnCOMInitialized += HandleCOMInitialized;
}
#endregion Constructor
#region Private Methods
/// Handles the initialized event raised from the Communication
private void HandleCOMInitialized()
{
OnInitialize?.Invoke(this, EventArgs.Emtpy);
}
#endregion Private Methods
#region Public Methods
/// Requests that the COM object be initialized
public void Initialize()
{
Communication.InitializeCOM();
}
#endregion Public Methods
}
The problem
Now, take a look at the Communication.StartCommunication() method, more specifically this part:
...
// [Place for a delaying logic]
...
If this line is substituted with the following:
await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false);
// OR
await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(true);
during inspection the final stop - Communication.InternalInitializeCOM() the apartment of the thread seems to be MTA.
However, if the delaying logic is changed to
Thread.Sleep(100);
the CommunicationInternalInitializeCOM() method seems to be executed in a STA state.
The inspection was done by Thread.CurrentThread.GetApartmentState().
The Question
Can anyone explain to me why does Task.Delay break the STA state? Or am I doing something else that is wrong here?
Thank you!
Thank you for taking all this time to read the question! Have a great day!
Hans has nailed it. Technically, your code is breaking because there's no SynchronizationContext captured by the await. But even if you write one, it won't be enough.
The one big problem with this approach is that your STA thread isn't pumping. STA threads must pump a Win32 message queue, or else they're not STA threads. SetApartmentState(ApartmentState.STA) is just telling the runtime that this is an STA thread; it doesn't make it an STA thread. You have to pump messages for it to be an STA thread.
You can write that message pump yourself, though I don't know of anyone brave enough to have done this. Most people install a message pump from WinForms (a la Hans' answer) or WPF. It may also be possible to do this with a UWP message pump.
One nice side effect of using the provided message pumps is that they also provide a SynchronizationContext (e.g., WinFormsSynchronizationContext / DispatcherSynchronizationContext), so await works naturally. Also, since every .NET UI framework defines a "run this delegate" Win32 message, the underlying Win32 message queue can also contain all the work you want to queue to your thread, so the explicit queue and its "runner" code is no longer necessary.
Because after await Task.Delay() statement , your code runs inside one of the ThreadPool thread, and since the ThreadPool threads are MTA by design.
var th = new Thread(async () =>
{
var beforAwait = Thread.CurrentThread.GetApartmentState(); // ==> STA
await Task.Delay(1000);
var afterAwait = Thread.CurrentThread.GetApartmentState(); // ==> MTA
});
th.SetApartmentState(ApartmentState.STA);
th.Start();

How to catch/observe an unhandled exception thrown from a Task

I'm trying to log / report all unhandled exceptions in my app (error reporting solution). I've come across a scenario that is always unhandled. I'm wondering how would I catch this error in an unhandled manner. Please note that I've done a ton of research this morning and tried a lot of things.. Yes, I've seen this, this and many more. I'm just looking for a generic solution to log unhandled exceptions.
I have the following code inside of a console test apps main method:
Task.Factory.StartNew(TryExecute);
or
Task.Run((Action)TryExecute);
as well as the following method:
private static void TryExecute() {
throw new Exception("I'm never caught");
}
I'm already tried wiring up to the following in my app, but they are never called.
AppDomain.CurrentDomain.UnhandledException
TaskScheduler.UnobservedTaskException
In my Wpf app where I initially found this error I also wired up to these events but it was never called.
Dispatcher.UnhandledException
Application.Current.DispatcherUnhandledException
System.Windows.Forms.Application.ThreadException
The only handler that is called ever is:
AppDomain.CurrentDomain.FirstChanceException
but this is not a valid solution as I only want to report uncaught exceptions (not every exception as FirstChanceException is called before any catch blocks are ever executed / resolved.
The TaskScheduler.UnobservedTaskException event should give you what you want, as you stated above. What makes you think that it is not getting fired?
Exceptions are caught by the task and then re-thrown, but not immediately, in specific situations. Exceptions from tasks are re-thrown in several ways (off the top of my head, there are probably more).
When you try and access the result (Task.Result)
Calling Wait(), Task.WaitOne(), Task.WaitAll() or another related Wait method on the task.
When you try to dispose the Task without explicitly looking at or handling the exception
If you do any of the above, the exception will be rethrown on whatever thread that code is running on, and the event will not be called since you will be observing the exception. If you don't have the code inside of a try {} catch {}, you will fire the AppDomain.CurrentDomain.UnhandledException, which sounds like what might be happening.
The other way the exception is re-thrown would be:
When you do none of the above so that the task still views the exception as unobserved and the Task is getting finalized. It is thrown as a last ditch effort to let you know there was an exception that you didn't see.
If this is the case and since the finalizer is non-deterministic, are you waiting for a GC to happen so that those tasks with unobserved exceptions are put in the finalizer queue, and then waiting again for them to be finalized?
EDIT: This article talks a little bit about this. And this article talks about why the event exists, which might give you insight into how it can be used properly.
I used the LimitedTaskScheduler from MSDN to catch all exceptions, included from other threads using the TPL:
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
{
/// Whether the current thread is processing work items.
[ThreadStatic]
private static bool currentThreadIsProcessingItems;
/// The list of tasks to be executed.
private readonly LinkedList tasks = new LinkedList(); // protected by lock(tasks)
private readonly ILogger logger;
/// The maximum concurrency level allowed by this scheduler.
private readonly int maxDegreeOfParallelism;
/// Whether the scheduler is currently processing work items.
private int delegatesQueuedOrRunning; // protected by lock(tasks)
public LimitedConcurrencyLevelTaskScheduler(ILogger logger) : this(logger, Environment.ProcessorCount)
{
}
public LimitedConcurrencyLevelTaskScheduler(ILogger logger, int maxDegreeOfParallelism)
{
this.logger = logger;
if (maxDegreeOfParallelism Gets the maximum concurrency level supported by this scheduler.
public override sealed int MaximumConcurrencyLevel
{
get { return maxDegreeOfParallelism; }
}
/// Queues a task to the scheduler.
/// The task to be queued.
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)
{
return;
}
++delegatesQueuedOrRunning;
NotifyThreadPoolOfPendingWork();
}
}
/// Attempts to execute the specified task on the current thread.
/// The task to be executed.
///
/// Whether the task could be executed 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)
{
TryDequeue(task);
}
// Try to run the task.
return TryExecuteTask(task);
}
/// Attempts to remove a previously scheduled task from the scheduler.
/// The task to be removed.
/// Whether the task could be found and removed.
protected sealed override bool TryDequeue(Task task)
{
lock (tasks)
{
return tasks.Remove(task);
}
}
/// Gets an enumerable of the tasks currently scheduled on this scheduler.
/// An enumerable of the tasks currently scheduled.
protected sealed override IEnumerable GetScheduledTasks()
{
var lockTaken = false;
try
{
Monitor.TryEnter(tasks, ref lockTaken);
if (lockTaken)
{
return tasks.ToArray();
}
else
{
throw new NotSupportedException();
}
}
finally
{
if (lockTaken)
{
Monitor.Exit(tasks);
}
}
}
protected virtual void OnTaskFault(AggregateException exception)
{
logger.Error(exception);
}
///
/// Informs the ThreadPool that there's work to be executed for this scheduler.
///
private void NotifyThreadPoolOfPendingWork()
{
ThreadPool.UnsafeQueueUserWorkItem(ExcuteTask, null);
}
private void ExcuteTask(object state)
{
// 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)
{
--delegatesQueuedOrRunning;
break;
}
// Get the next item from the queue
item = tasks.First.Value;
tasks.RemoveFirst();
}
// Execute the task we pulled out of the queue
TryExecuteTask(item);
if (!item.IsFaulted)
{
continue;
}
OnTaskFault(item.Exception);
}
}
finally
{
// We're done processing items on the current thread
currentThreadIsProcessingItems = false;
}
}
}
And than the "registration" of the TaskScheduler as the default using Reflection:
public static class TaskLogging
{
private const BindingFlags StaticBinding = BindingFlags.Static | BindingFlags.NonPublic;
public static void SetScheduler(TaskScheduler taskScheduler)
{
var field = typeof(TaskScheduler).GetField("s_defaultTaskScheduler", StaticBinding);
field.SetValue(null, taskScheduler);
SetOnTaskFactory(new TaskFactory(taskScheduler));
}
private static void SetOnTaskFactory(TaskFactory taskFactory)
{
var field = typeof(Task).GetField("s_factory", StaticBinding);
field.SetValue(null, taskFactory);
}
}

How does nunit successfully wait for async void methods to complete?

When using async/await in C#, the general rule is to avoid async void as that's pretty much a fire and forget, rather a Task should be used if no return value is sent from the method. Makes sense. What's strange though is that earlier in the week I was writing some unit tests for a few async methods I wrote, and noticed that NUnit suggested to mark the async tests as either void or returning Task. I then tried it, and sure enough, it worked. This seemed really strange, as how would the nunit framework be able to run the method and wait for all asynchronous operations to complete? If it returns Task, it can just await the task, and then do what it needs to do, but how can it pull it off if it returns void?
So I cracked open the source code and found it. I can reproduce it in a small sample, but I simply cannot make sense of what they're doing. I guess I don't know enough about the SynchronizationContext and how that works. Here's the code:
class Program
{
static void Main(string[] args)
{
RunVoidAsyncAndWait();
Console.WriteLine("Press any key to continue. . .");
Console.ReadKey(true);
}
private static void RunVoidAsyncAndWait()
{
var previousContext = SynchronizationContext.Current;
var currentContext = new AsyncSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(currentContext);
try
{
var myClass = new MyClass();
var method = myClass.GetType().GetMethod("AsyncMethod");
var result = method.Invoke(myClass, null);
currentContext.WaitForPendingOperationsToComplete();
}
finally
{
SynchronizationContext.SetSynchronizationContext(previousContext);
}
}
}
public class MyClass
{
public async void AsyncMethod()
{
var t = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
Console.WriteLine("Done sleeping!");
});
await t;
Console.WriteLine("Done awaiting");
}
}
public class AsyncSynchronizationContext : SynchronizationContext
{
private int _operationCount;
private readonly AsyncOperationQueue _operations = new AsyncOperationQueue();
public override void Post(SendOrPostCallback d, object state)
{
_operations.Enqueue(new AsyncOperation(d, state));
}
public override void OperationStarted()
{
Interlocked.Increment(ref _operationCount);
base.OperationStarted();
}
public override void OperationCompleted()
{
if (Interlocked.Decrement(ref _operationCount) == 0)
_operations.MarkAsComplete();
base.OperationCompleted();
}
public void WaitForPendingOperationsToComplete()
{
_operations.InvokeAll();
}
private class AsyncOperationQueue
{
private bool _run = true;
private readonly Queue _operations = Queue.Synchronized(new Queue());
private readonly AutoResetEvent _operationsAvailable = new AutoResetEvent(false);
public void Enqueue(AsyncOperation asyncOperation)
{
_operations.Enqueue(asyncOperation);
_operationsAvailable.Set();
}
public void MarkAsComplete()
{
_run = false;
_operationsAvailable.Set();
}
public void InvokeAll()
{
while (_run)
{
InvokePendingOperations();
_operationsAvailable.WaitOne();
}
InvokePendingOperations();
}
private void InvokePendingOperations()
{
while (_operations.Count > 0)
{
AsyncOperation operation = (AsyncOperation)_operations.Dequeue();
operation.Invoke();
}
}
}
private class AsyncOperation
{
private readonly SendOrPostCallback _action;
private readonly object _state;
public AsyncOperation(SendOrPostCallback action, object state)
{
_action = action;
_state = state;
}
public void Invoke()
{
_action(_state);
}
}
}
When running the above code, you'll notice that the Done Sleeping and Done awaiting messages show up before the Press any key to continue message, which means the async method is somehow being waited on.
My question is, can someone care to explain what's happening here? What exactly is the SynchronizationContext (I know it's used to post work from one thread to another) but I'm still confused as to how we can wait for all the work to be done. Thanks in advance!!
A SynchronizationContext allows posting work to a queue that is processed by another thread (or by a thread pool) -- usually the message loop of the UI framework is used for this.
The async/await feature internally uses the current synchronization context to return to the correct thread after the task you were waiting for has completed.
The AsyncSynchronizationContext class implements its own message loop. Work that is posted to this context gets added to a queue.
When your program calls WaitForPendingOperationsToComplete();, that method runs a message loop by grabbing work from the queue and executing it.
If you set a breakpoint on Console.WriteLine("Done awaiting");, you will see that it runs on the main thread within the WaitForPendingOperationsToComplete() method.
Additionally, the async/await feature calls the OperationStarted() / OperationCompleted() methods to notify the SynchronizationContext whenever an async void method starts or finishes executing.
The AsyncSynchronizationContext uses these notifications to keep a count of the number of async methods that are running and haven't completed yet. When this count reaches zero, the WaitForPendingOperationsToComplete() method stops running the message loop, and the control flow returns to the caller.
To view this process in the debugger, set breakpoints in the Post, OperationStarted and OperationCompleted methods of the synchronization context. Then step through the AsyncMethod call:
When AsyncMethod is called, .NET first calls OperationStarted()
This sets the _operationCount to 1.
Then the body of AsyncMethod starts running (and starts the background task)
At the await statement, AsyncMethod yields control as the task is not yet complete
currentContext.WaitForPendingOperationsToComplete(); gets called
No operations are available in the queue yet, so the main thread goes to sleep at _operationsAvailable.WaitOne();
On the background thread:
at some point the task finishes sleeping
Output: Done sleeping!
the delegate finishes execution and the task gets marked as complete
the Post() method gets called, enqueuing a continuation that represents the remainder of the AsyncMethod
The main thread wakes up because the queue is no longer empty
The message loop runs the continuation, thus resuming execution of AsyncMethod
Output: Done awaiting
AsyncMethod finishes execution, causing .NET to call OperationComplete()
the _operationCount is decremented to 0, which marks the message loop as complete
Control returns to the message loop
The message loop finishes because it was marked as complete, and WaitForPendingOperationsToComplete returns to the caller
Output: Press any key to continue. . .

Using BlockingCollection<T>: OperationCanceledException, is there a better way?

I'm making use of the (frankly great) BlockingCollection<T> type for a heavily multithreaded, high-performance app.
There's a lot of throughput through the collection and on the micro-level it's highly performant. However, for each 'batch' it will always be ended by flagging the cancellation token. This results in an exception being thrown on any waiting Take call. That's fine, but I would have settled for a return value or output parameter to signal it, because a) exceptions have an obvious overhead and b) when debugging, I don't want to manually turn off break-on-exception for that specific exception.
The implementation seems intense, and in theory I suppose I could disassemble and recreate my own version that didn't use exceptions, but perhaps there's a less complex way?
I could add a null (or if not, a placeholder) object to the collection to signify the process should end, however there also needs to be a means to abort nicely, i.e. wake up waiting threads and tell them somehow that something's gone on.
So - alternative collection types? Recreate my own? Some way to abuse this one?
(Some context: I went with BlockingCollection<T> because it has an advantage over manual locking around a Queue. As best I can tell the use of threading primitives is superb and in my case, a few milliseconds here-and-there and optimal core is use crucial.)
Edit: I've just opened a bounty for this one. I don't believe Anastasiosyal's answer covers the query I raise in my comment of it. I know this is a tough problem. Is anyone able to assist?
As I guess you have already done yourself, looking into the reflected source of BlockingCollection it looks unfortunately that when a CancellationToken is passed into the BlockingCollection and it cancels then you will get the OperationCancelledException as can be seen in the image below (with a couple of workarounds after the image)
GetConsumingEnumerable invokes TryTakeWithNoTimeValidation on the BlockingCollection which in turn raises this exception.
Workaround #1
One potential strategy would be, assuming you have more control over your producers and your consumers, rather than pass the cancellation token into the BlockingCollection, (which will raise this exception) you pass the cancellation token into your producers and into your consumers.
If your producers aren't producing and your consumers aren't consuming, then you have effectively cancelled the operation without raising this exception and by passing CancellationToken.None in your BlockingCollection.
Special cases Cancelling when the BlockingCollection is at BoundedCapacity or Empty
Producers blocked: The producer threads will be blocked when BoundedCapacity on the BlockingCollection is reached. Hence, when attempting to cancel and the BlockingCollection is at BoundedCapacity (which means that your consumers are not blocked but producers are blocked because they cannot add any additional items to the queue) then you will need to allow for additional items to be consumed (one for each producer thread) that will unblock the producers (because they are blocked on adding to the blockingCollection) and in turn allow for your cancellation logic to kick in on the producer side.
Consumers blocked: When your consumers are blocked because the queue is empty, then you could insert an empty unit of work (one for each consumer thread) in the Blocking collection so as to unblock the consumer threads and allow for your cancellation logic to kick in the consumer side.
When there are items in the queue and no limit such as BoundedCapacity or Empty has been reached then the producers and consumer threads should not be blocked.
Workaround #2
Using a cancellation unit of work.
When your application needs to cancel, then your producers (maybe just 1 producer will suffice while the others just cancel producing) will produce a cancellation unit of work (could be null as you also mention or some class that implements a marker interface). When the consumers consume this unit of work and detect that it is in fact a cancellation unit of work, their cancellation logic kicks in. The number of cancellation units of work to be produced needs to equal the number of consumer threads.
Again, caution is needed when we are close to BoundedCapacity, as it could be a sign that some of the producers are blocked. Depending on the number of producers/consumers you could have a consumer consuming until all producers (but 1) have shut down. This ensures that there are no lingering producers around. When there is only 1 producer left, your last consumer can shut down and the producer can stop producing cancellation units of work.
How about the BlockingQueue I did a while ago?
http://apichange.codeplex.com/SourceControl/changeset/view/76c98b8c7311#ApiChange.Api%2fsrc%2fInfrastructure%2fBlockingQueue.cs
It should do fine without any exceptions. The current queue does simply close the event on dispose which might not be what you want. You might want do enque a null and wait until all items were processed. Apart from this it should suit your needs.
using System.Collections.Generic;
using System.Collections;
using System.Threading;
using System;
namespace ApiChange.Infrastructure
{
/// <summary>
/// A blocking queue which supports end markers to signal that no more work is left by inserting
/// a null reference. This constrains the queue to reference types only.
/// </summary>
/// <typeparam name="T"></typeparam>
public class BlockingQueue<T> : IEnumerable<T>, IEnumerable, IDisposable where T : class
{
/// <summary>
/// The queue used to store the elements
/// </summary>
private Queue<T> myQueue = new Queue<T>();
bool myAllItemsProcessed = false;
ManualResetEvent myEmptyEvent = new ManualResetEvent(false);
/// <summary>
/// Deques an element from the queue and returns it.
/// If the queue is empty the thread will block. If the queue is stopped it will immedieately
/// return with null.
/// </summary>
/// <returns>An object of type T</returns>
public T Dequeue()
{
if (myAllItemsProcessed)
return null;
lock (myQueue)
{
while (myQueue.Count == 0)
{
if(!Monitor.Wait(myQueue, 45))
{
// dispatch any work which is not done yet
if( myQueue.Count > 0 )
continue;
}
// finito
if (myAllItemsProcessed)
{
return null;
}
}
T result = myQueue.Dequeue();
if (result == null)
{
myAllItemsProcessed = true;
myEmptyEvent.Set();
}
return result;
}
}
/// <summary>
/// Releases the waiters by enqueuing a null reference which causes all waiters to be released.
/// The will then get a null reference as queued element to signal that they should terminate.
/// </summary>
public void ReleaseWaiters()
{
Enqueue(null);
}
/// <summary>
/// Waits the until empty. This does not mean that all items are already process. Only that
/// the queue contains no more pending work.
/// </summary>
public void WaitUntilEmpty()
{
myEmptyEvent.WaitOne();
}
/// <summary>
/// Adds an element of type T to the queue.
/// The consumer thread is notified (if waiting)
/// </summary>
/// <param name="data_in">An object of type T</param>
public void Enqueue(T data_in)
{
lock (myQueue)
{
myQueue.Enqueue(data_in);
Monitor.PulseAll(myQueue);
}
}
/// <summary>
/// Returns an IEnumerator of Type T for this queue
/// </summary>
/// <returns></returns>
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
while (true)
{
T item = Dequeue();
if (item == null)
break;
else
yield return item;
}
}
/// <summary>
/// Returns a untyped IEnumerator for this queue
/// </summary>
/// <returns></returns>
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<T>)this).GetEnumerator();
}
#region IDisposable Members
/// <summary>
/// Closes the EmptyEvent WaitHandle.
/// </summary>
public void Dispose()
{
myEmptyEvent.Close();
}
#endregion
}
}
You cound signal the end of a batch by setting a flag on the last item (add a IsLastItem bool property to it or wrap it). Or you might send a null as last item (not sure if a null goes through the blockingcollection correctly though).
If you can remove the need for the 'batch' concept you can create an extra thread to continously Take() and Process new Data from your blockingcollection and do nothing else.
Kieren,
From my inspection, I personally don't know any thread safe type for ProducerConsumer pattern which does exactly what you wanted. I don't claim this as competitive solution but propose you decorate BlockingCollection<T> with few extension method which will give you the freedom to supply any built-in or custom types instead of default CancellationToken.
Stage 1:
Following are the list of default method which use underling TryAddWithNoTimeValidation method to add to queue.
public void Add(T item){
this.TryAddWithNoTimeValidation(item, -1, new CancellationToken());
}
public void Add(T item, CancellationToken cancellationToken){
this.TryAddWithNoTimeValidation(item, -1, cancellationToken);
}
public bool TryAdd(T item){
return this.TryAddWithNoTimeValidation(item, 0, new CancellationToken());
}
public bool TryAdd(T item, TimeSpan timeout){
BlockingCollection<T>.ValidateTimeout(timeout);
return this.TryAddWithNoTimeValidation(item, (int) timeout.TotalMilliseconds, new CancellationToken());
}
public bool TryAdd(T item, int millisecondsTimeout){
BlockingCollection<T>.ValidateMillisecondsTimeout(millisecondsTimeout);
return this.TryAddWithNoTimeValidation(item, millisecondsTimeout, new CancellationToken());
}
public bool TryAdd(T item, int millisecondsTimeout, CancellationToken cancellationToken){
BlockingCollection<T>.ValidateMillisecondsTimeout(millisecondsTimeout);
return this.TryAddWithNoTimeValidation(item, millisecondsTimeout, cancellationToken);
}
Now you can provide extension for any/all of method which you are interested.
Stage 2:
You now refer your implementation of TryAddWithNoTimeValidation instead of default.
I can give you an alternate version of TryAddWithNoTimeValidation which safely continue without throwing OperationCancellation exception.
My suggestion is to implement this functionality by encapsulating an asynchronous queue, like the BufferBlock<T> class from the TPL Dataflow library. This class is a thread-safe container intended for producer-consumer scenarios, and supports backpressure (BoundedCapacity) just like the BlockingCollection<T> class. Being asynchronous means that the corresponding Add/Take methods (SendAsync/ReceiveAsync) return tasks. These tasks store the event of a cancellation as an internal state, that can be queried with the IsCanceled property, so throwing exceptions internally can be avoided. Propagating this state with exceptions can also be avoided, by waiting the tasks using a exception-suppressing continuation (ContinueWith). Here is an implementation:
/// <summary>
/// A thread-safe collection that provides blocking and bounding capabilities.
/// The cancellation is propagated as a false result, and not as an exception.
/// </summary>
public class CancellationFriendlyBlockingCollection<T>
{
private readonly BufferBlock<T> _bufferBlock;
public CancellationFriendlyBlockingCollection()
{
_bufferBlock = new BufferBlock<T>();
}
public CancellationFriendlyBlockingCollection(int boundedCapacity)
{
_bufferBlock = new BufferBlock<T>(new() { BoundedCapacity = boundedCapacity });
}
public bool TryAdd(T item, CancellationToken cancellationToken = default)
{
if (cancellationToken.IsCancellationRequested) return false;
if (_bufferBlock.Post(item)) return true;
Task<bool> task = _bufferBlock.SendAsync(item, cancellationToken);
WaitNoThrow(task);
if (!task.IsCompletedSuccessfully) return false;
return task.Result;
}
public bool TryTake(out T item, CancellationToken cancellationToken = default)
{
if (cancellationToken.IsCancellationRequested) { item = default; return false; }
if (_bufferBlock.TryReceive(out item)) return true;
Task<T> task = _bufferBlock.ReceiveAsync(cancellationToken);
WaitNoThrow(task);
if (!task.IsCompletedSuccessfully) return false;
item = task.Result; return true;
}
public IEnumerable<T> GetConsumingEnumerable(
CancellationToken cancellationToken = default)
{
while (TryTake(out var item, cancellationToken)) yield return item;
}
public void CompleteAdding() => _bufferBlock.Complete();
public bool IsCompleted => _bufferBlock.Completion.IsCompleted;
public int Count => _bufferBlock.Count;
// Wait the task to complete without throwing exceptions
private static void WaitNoThrow(Task task)
{
if (task.IsCompleted) return;
task.ContinueWith(_ => { }, default,
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default).Wait();
Debug.Assert(task.IsCompleted);
}
}
Performance: The CancellationFriendlyBlockingCollection.TryTake method can be invoked with a canceled CancellationToken in a loop with a frequency of about 15,000,000 times per second in my PC (on a single thread). For comparison the frequency of the BlockingCollection<T>.Take under the same conditions is about 20,000 times per second.
You might be tempted to replace the BufferBlock<T> with a more modern asynchronous queue like the Channel<T>. In that case please make sure to read this question first, in order to be aware about a leaky behavior of this class, under specific conditions.

WatiN Parallelization within a test

I have a test where I want to ensure separate operations within one page result in distinct results. Specifically, I have a few ways to sort on a page and I want a test to make sure that each sort is different. I have other tests to ensure the correctness of each sort.
I would like the focus of this conversation to be on a good way to run test operations in parallel and compare the results at the end, rather than on what to test or testing methods. I figure parallel operations in testing is an interesting and broad enough topic that it could be useful to others.
Let "generateHashFromSearchResults()" be a function that returns a string representing the order of the search results shown on current IE instance. Here is what the working code looks like in a serialized fashion using one browser instance:
var set = new HashSet<string>();
var sortOptions = new List<String>() { "sort1", "sort2", "sort3" };
// Default sort
set.Add(generateHashFromSearchResults());
sortOptions.ForEach(s => {
ie.Link(Find.ByText(s)).Click();
set.Add(generateHashFromSearchResults());
});
Assert.That(set.Count() == 4);
I had read about PLINQ a few months ago and figured this might be a decent use case. Now let "generateHashFromSearchResults(IE ie)" be the same function, but that operates on an explicitly defined IE instance. I tried something like this:
List<string> resultList = sortOptions.AsParallel().Select(s => {
var ie = new IE(true);
ie.Link(Find.ByText(s)).Click();
return generateHashFromSearchResults(ie);
}).ToList();
// Forget about default sort for now. There should be 3 distinct results
Assert.That(new HashSet<string>(resultList).Count() == 3);
The biggest issue I face right now is not understanding how PLINQ does thread management. WatiN needs to run with the apartment state set to single threaded (STAThread). I get that each IE instance should be in its own thread, but no amount of setting each thread in the PLINQ query to the proper apartment state fixes the issue.
I'm starting to suspect that I either need to learn more about PLINQ to continue, or that I need to learn more about thread management by hand to get this to work.
Any thoughts?
You can't specify a custom scheduler with AsParallel(). But you can create a Task for each sort option and pass an instance of a custom scheduler into the Start() method. This implementation of an STA Thread scheduler was borrowed from Stephen Toub (http://blogs.msdn.com/b/pfxteam/archive/2010/04/07/9990421.aspx):
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// Provides a scheduler that uses STA threads.
/// Borrowed from Stephen Toub's implementation http://blogs.msdn.com/b/pfxteam/archive/2010/04/07/9990421.aspx
/// </summary>
public sealed class StaTaskScheduler : TaskScheduler, IDisposable
{
/// <summary>
/// The STA threads used by the scheduler.
/// </summary>
private readonly List<Thread> threads;
/// <summary>
/// Stores the queued tasks to be executed by our pool of STA threads.
/// </summary>
private BlockingCollection<Task> tasks;
/// <summary>
/// Initializes a new instance of the StaTaskScheduler class with the specified concurrency level.
/// </summary>
/// <param name = "numberOfThreads">The number of threads that should be created and used by this scheduler.</param>
public StaTaskScheduler(int numberOfThreads)
{
if (numberOfThreads < 1)
{
throw new ArgumentOutOfRangeException(
"numberOfThreads", "The scheduler must create at least one thread");
}
// Initialize the tasks collection
this.tasks = new BlockingCollection<Task>();
// Create the threads to be used by this scheduler
this.threads = Enumerable.Range(0, numberOfThreads).Select(
i =>
{
var thread = new Thread(
() =>
{
// Continually get the next task and try to execute it.
// This will continue until the scheduler is disposed and no more tasks remain.
foreach (Task t in this.tasks.GetConsumingEnumerable())
{
this.TryExecuteTask(t);
}
}) {
Name = "Sta Thread", IsBackground = true
};
thread.SetApartmentState(ApartmentState.STA);
return thread;
}).ToList();
// Start all of the threads
this.threads.ForEach(t => t.Start());
}
/// <summary>
/// Gets the maximum concurrency level supported by this scheduler.
/// </summary>
public override int MaximumConcurrencyLevel
{
get
{
return this.threads.Count;
}
}
/// <summary>
/// Cleans up the scheduler by indicating that no more tasks will be queued.
/// This method blocks until all threads successfully shutdown.
/// </summary>
public void Dispose()
{
if (this.tasks != null)
{
// Indicate that no new tasks will be coming in
this.tasks.CompleteAdding();
// Wait for all threads to finish processing tasks
foreach (Thread thread in this.threads)
{
thread.Join();
}
// Cleanup
this.tasks.Dispose();
this.tasks = null;
}
}
/// <summary>
/// Provides a list of the scheduled tasks for the debugger to consume.
/// </summary>
/// <returns>An enumerable of all tasks currently scheduled.</returns>
protected override IEnumerable<Task> GetScheduledTasks()
{
// Serialize the contents of the blocking collection of tasks for the debugger
return this.tasks.ToArray();
}
/// <summary>
/// Queues a Task to be executed by this scheduler.
/// </summary>
/// <param name = "task">The task to be executed.</param>
protected override void QueueTask(Task task)
{
// Push it into the blocking collection of tasks
this.tasks.Add(task);
}
/// <summary>
/// Determines whether a Task may be inlined.
/// </summary>
/// <param name = "task">The task to be executed.</param>
/// <param name = "taskWasPreviouslyQueued">Whether the task was previously queued.</param>
/// <returns>true if the task was successfully inlined; otherwise, false.</returns>
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// Try to inline if the current thread is STA
return Thread.CurrentThread.GetApartmentState() == ApartmentState.STA && this.TryExecuteTask(task);
}
}
Maybe You should use Task Parallel Library?
I'm a beginner in TPL, but there are Schedulers that maybe have some options for setting STAThread on scheduled Tasks.

Categories

Resources