using System; using System.Collections.Concurrent; using System.Threading; namespace RageCoop.Core { /// /// A worker that constantly execute jobs in a background thread. /// public class Worker : IDisposable { private readonly SemaphoreSlim _semaphoreSlim; private readonly Thread _workerThread; private bool _stopping = false; /// /// Name of the worker /// public string Name { get; set; } /// /// Whether this worker is busy executing job(s). /// public bool IsBusy { get; private set; } internal Worker(string name, Logger logger, int maxJobs = Int32.MaxValue) { Name = name; _semaphoreSlim = new SemaphoreSlim(0, maxJobs); _workerThread = new Thread(() => { while (!_stopping) { IsBusy = false; _semaphoreSlim.Wait(); if (Jobs.TryDequeue(out var job)) { IsBusy = true; try { job.Invoke(); } catch (Exception ex) { logger.Error("Error occurred when executing queued job:"); logger.Error(ex); } } else { throw new InvalidOperationException("Hmm... that's unexpected."); } } IsBusy = false; }); _workerThread.Start(); } /// /// Queue a job to be executed /// /// public void QueueJob(Action work) { Jobs.Enqueue(work); _semaphoreSlim.Release(); } /// /// Finish current job and stop the worker. /// public void Stop() { _stopping = true; QueueJob(() => { }); if (_workerThread.IsAlive) { _workerThread.Join(); } } /// /// Finish current job and stop the worker. /// public void Dispose() { Stop(); _semaphoreSlim.Dispose(); } private readonly ConcurrentQueue Jobs = new ConcurrentQueue(); } }