A simple item queue for executing actions and/or delegates with enqueued data on a background thread.
- Provides three different queue types: ActionQueue, FunctionQueue, and PredicateQueue (explained in the Usage section)
- Runs on a background thread
- Uses concurrent queues for thread safety
- Uses thread synchronization events to prevent having to poll the queue
- Provides events and callbacks for keeping the caller informed of the queue's progress
- Implements IDisposable
- All public methods are full documented
- Unit tests included for all three queues which demonstrates additional features and usage patterns.
This library provides three queues depending on the application's specific needs. These queues are:
- ActionQueue
- FunctionQueue
- PredicateQueue
Each of these will be discussed in the following sections.
This queue is used to execute the specified action on each member that is added to the queue. The following example sums all the items in the queue:
var sum = 0;
var actionQueue = new ActionQueue<int>();
actionQueue.SetAction(value => sum += value);
actionQueue.Start();
actionQueue.Enqueue(1);
actionQueue.Enqueue(2);
actionQueue.Stop(true);
In both of these example, the value of sum
will be 3
after the queue finishes processing the items.
The function queue can be used to apply a user-defined function to the items in the queue. The specified action will then be called with the result of the function call. In the following example, the specified function will double the value of the item in the queue. The action will then be called to retrieve this value:
var result = 0;
var functionQueue = new FunctionQueue<int, int>();
functionQueue.SetFunction(value => value * 2);
functionQueue.SetAction(value => result = value);
functionQueue.Start();
functionQueue.Enqueue(1);
functionQueue.Stop(true);
In the above example, the value of result
will be 2
since the specified function doubles the value in the queue (which only a single value of 1
was enqueued) and
the action just sets result
to the value passed to it from the result of the function call.
The predicate queue is a specialized function-type queue. For the predicate queue, you specify a function (i.e. a predicate) which returns a bool
. If the result of the predicate
is true
then the value that was enqueued is passed to the specified action:
var sum = 0;
var predicateQueue = new PredicateQueue<int>();
predicateQueue.SetAction(value => sum += value);
predicateQueue.SetPredicate(value => value % 2 == 0);
predicateQueue.Enqueue(1);
predicateQueue.Enqueue(2);
predicateQueue.Enqueue(3);
predicateQueue.Enqueue(4);
predicateQueue.Enqueue(5);
predicateQueue.Enqueue(6);
predicateQueue.Enqueue(7);
predicateQueue.Enqueue(8);
predicateQueue.Enqueue(9);
predicateQueue.Enqueue(10);
predicateQueue.Start();
predicateQueue.Stop(true);
In the above example, the value of sum
will be 30
. This is because the specified predicate only returns true
for even numbers. Therefore the specified action will only perform a sum
of the even numbers in the queue (i.e. 2 + 4 + 6 + 8 + 10 == 30). If the odd numbers needed to be summed, the predicate could just be changed to predicateQueue.SetPredicate(value => value % 2 != 0);
with everything else remaining the same.
All queues also support callbacks when the specified action completed without throwing any exceptions. The callbacks for the ActionQueue<T>
and PredicateQueue<T>
queues work exactly the same way.
The following is an example of a callback being uses just to sum the results of the queue with a separate variable:
var sum = 0;
var callbackSum = 0;
var actionQueue = new ActionQueue<int>();
actionQueue.SetAction(value => sum += value);
actionQueue.SetCallback(value => callbackSum += value);
actionQueue.Start();
actionQueue.Enqueue(1);
actionQueue.Enqueue(2);
actionQueue.Stop(true);
In above example, both the value of sum
and callbackSum
will be 3
.
However, the callback of the FunctionQueue<T>
as the signature of Action<T, TResult>
. This is because it includes the original value that was enqueued as well as the result of the specified function
(which is passed to the specified action). Therefore, it is possible to see the before and after values for any necessary purposes (e.g. debugging).
var result = 0;
var callbackInitialValue = 0;
var callbackResultValue = 0;
var functionQueue = new FunctionQueue<int, int>();
functionQueue.SetFunction(value => value * 2);
functionQueue.SetCallback((i, r) =>
{
callbackInitialValue = i;
callbackResultValue = r;
});
functionQueue.SetAction(value => result = value);
functionQueue.Start();
functionQueue.Enqueue(1);
functionQueue.Stop(true);
In the above example, the values of the variables is as follows:
result
is2
because the function doubles the enqueued value of1
and passes it to the specified actioncallbackInitialValue
is1
because this was the enqueued value that was passed to the specified functioncallbackResultValue
is2
as this was the result of the function
To stop a queue, the queue's Stop()
method needs to be called. This method takes one argument named processRemainingItems
. If the value of processRemainingItems
is true
, the queue will finish
processing any items remaining the queue. However, if the value is false
any items still in the queue will be discarded before completing its shutdown.
The queues can raise three different events:
StatusChanged
: This event is raised when the status of the queue has changed (e.g. from waiting for new data to processing new items)ActionCompleted
: This event is raised when the specified action has been completed on a specified action and contains the data that was passed to the action, whether the action completed successfully, and a possible message for more information. This event can be used in place of callbacks in certain use-cases.OnErrorOccurred
: This event is raised when an exception occurs during the processing of times. When possible, it will contain the specific items that caused an exception, a message, and theException
that was thrown.
All three queues manage their data using threads in order to not block the main UI thread. In order to prevent any threads from handing around longer than needed, all the queues implement the IDisposable
interface, which makes sure that the threads and queues are cleaned up when they are no longer needed. Therefore, where possible, it is best to use a using
block:
var sum = 0;
using(var actionQueue = new ActionQueue<int>())
{
actionQueue.SetAction(value => sum += value);
actionQueue.Start();
actionQueue.Enqueue(1);
actionQueue.Enqueue(2);
actionQueue.Stop(true);
}
At the end of the using
, the threads will be terminated and the queues cleaned up. In use-cases where a using
block is not appropriate, the Stop()
method should be called when processing can be
halted and the Dispose()
method should be called when the queue should be deconstructed.
All the queues support some advanced features in regards to the threading aspect of the library, specifically:
- Queue Name: A queue name can be specified using one of the overloaded constructors. This name cannot be changed once set and is used as the queue's thread's name. If name of the queue that was set can be retrieved via the read-only
Name
property. - Thread Properties:
- Background: The
Start()
method has overloads, some of which accept anisBackground
parameter. This specifies whether or not the queue's thread is a background thread or not. When not specified, it is set totrue
by default. - Priority: Some of the
Start()
method overloads also accept aThreadPriority
enum which specifies the queue's thread priority. When not specified, the thread priority is set to normal (i.e.ThreadPriority.Normal
)
- Background: The
Contributing to this project is welcome. However, we ask that you please follow our contributing guidelines to help ensure consistency.
0.1.0 - Initial Release
- Please note that this is a PREVIEW version and there is no guarantee that any method signatures or functionality will remaing the same
This project uses SemVer for versioning.
- Dominick Marciano Jr.
Copyright (c) 2019 Dominick Marciano Jr., Sci-Med Coding. All rights reserved
See LICENSE for full licesning terms.