JobQueue

Description
This is a simple threaded job queue for any kind jobs you want to execute on a thread.

The JobQueue class is a management class, the actual job scheduler. When you create an instance of that class you have to specify the "job class" it will manage as generic parameter and how many threads the queue should use.

The threads are started immediately when the JobQueue is created and put on hold until jobs are queued.

JobQueue
- JobQueue(int aThreadCount) - constructor. - AddJob(T aJob) - Adds a job item to the queue for processing. If there are unused threads available it will immediately schedule the job. - AddJobFromOtherThreads(T aJob) - This has to be used when you want to schedule new jobs from other threads than the main thread. - int CountActiveJobs - counts the currently active threads. - void Update - Processes the queue on the main thread. Schedules waiting jobs and "finishes" jobs which are done. - void ShutdownQueue - terminates all active jobs and terminates all running threadd. After using this method the the JobQueue can no longer be used. - event OnJobFinished - An event to which you can subscribe from the outside. It will be invoked each time a job finishes. The callback has the JobItem that has finished as parameter. This event is raised on the main thread (from inside "Update").

JobItem
- bool IsAborted - readonly property to determine if this job should be aborted. The actual work routine should check this on a regular basis while executing and terminate itself immediately when this is true - bool IsDataReady - readonly property to indicate that the job has finished. You may not touch any of the Job data while this is not true after you handed the job to the queue. - abstract void DoWork - This is the work routine which has to be overriden in a concrete Job class. - virtual void OnFinished - This is a callback which will be invoked from the main thread (the thread that  executed Update on the JobQueue) once the DoWork method has finished. - void AbortJob - signals that this job should be aborted. This is called internally when the Jobqueue is  shutdown and a job is still running.

- void Execute and void ResetJobState are used internally by the JobQueue. You should not call these.

Of course the JobItem instances could be pooled if needed. Just keep in mind to only touch an JobItem instance when it's not in use.

Additional usage notes
This implementation avoids locks where possible. It uses a well defined order of instructions in conjunction with state flags to ensure no race conditions when used properly.

This class is primarily designed for a "single producer, single consumer" layout. So Jobs are only added to the queue from the main thread (the thread that calls Update on the queue). The class supports adding jobs from other threads by using "AddJobFromOtherThreads" instead of "AddJob". This will store the job in an additional "locked" queue which then will be copied into the actual job queue.

Important: The moment you call AddJob (or AddJobFromOtherThreads), you hand over the job item to the scheduler and you should no longer touch any of the data inside the job item until IsDataReady turns true. Usually you would use one of the callbacks to further process the jobitem on the main thread once it's finished. This can be implemented directly inside the JobItem by overriding "OnFinished", or in a more general manner by subscribing to the "OnJobFinished" event in the JobQueue. Those callbacks will be invoked from inside the Update method. So as long as Update is only called from the main thread those callbacks will always be executed on the main thread.

Do not call Update from multiple threads!!! One JobQueue should only be managed by a single thread.