[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/tgoessler/TCSystemCS/develop/Thread/WorkerThread.cs [Back]  [Original]

// *******************************************************************************
// 
//  *******   ***   ***               *
//     *     *     *                  *
//     *    *      *                *****
//     *    *       ***  *   *   **   *    **    ***
//     *    *          *  * *   *     *   ****  * * *
//     *     *         *   *      *   * * *     * * *
//     *      ***   ***    *     **   **   **   *   *
//                         *
// *******************************************************************************
//  see https://github.com/tgoessler/TCSystemCS for details.
//  Copyright (C) 2003 - 2026 Thomas Goessler. All Rights Reserved.
// *******************************************************************************
// 
//  TCSystem is the legal property of its developers.
//  Please refer to the COPYRIGHT file distributed with this source distribution.
// 
// *******************************************************************************

#region Usings

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

#endregion

namespace TCSystem.Thread;

internal sealed class WorkerThread : IWorkerThread
{
#region Public

    public WorkerThread(string name, ThreadPriority priority)
    {
        _thread = new(RunThread)
        {
            Name = name,
            Priority = priority
        };
        _running = true;
        _thread.Start();
    }

    public void ExecuteCommand(Action action)
    {
        ExecuteCommand(action, null);
    }

    public void ExecuteCommand(Action action, string message)
    {
        using (_semaphoreSlim.Lock())
        {
            _workerActions.Enqueue((action, message));
            _event.Set();
        }
    }

    public void ClearOpenCommands()
    {
        using (_semaphoreSlim.Lock())
        {
            _workerActions.Clear();
        }
    }

    public async Task ExecuteCommandAsync(Action action)
    {
        await ExecuteCommandAsync(action, null);
    }

    public async Task ExecuteCommandAsync(Action action, string message)
    {
        using (await _semaphoreSlim.LockAsync())
        {
            _workerActions.Enqueue((action, message));
            _event.Set();
        }
    }

    public async Task ClearOpenCommandsAsync()
    {
        using (await _semaphoreSlim.LockAsync())
        {
            _workerActions.Clear();
        }
    }

    public void StopThread(TimeSpan? timeOut = null)
    {
        if (_thread is { IsAlive: true })
        {
            Log.Instance.Info($"Stopping Thread '{_thread.Name}'");

            using (_semaphoreSlim.Lock())
            {
                _workerActions.Clear();
                _running = false;
                _cancellationTokenSource.Cancel();
                _event.Set();
            }

            if (timeOut == null)
            {
                _thread.Join();
            }
            else
            {
                _thread.Join(timeOut.Value);
            }

            _cancellationTokenSource.Dispose();
            Log.Instance.Info($"Stopping Thread '{_thread.Name} done'");
        }
    }

    public event Action OnInitThread;
    public event Action OnDeInitThread;
    public event Action IdleEvent;

    public int NumOpenActions
    {
        get
        {
            using (_semaphoreSlim.Lock())
            {
                return _workerActions.Count;
            }
        }
    }

    public bool IsBusy
    {
        get
        {
            using (_semaphoreSlim.Lock())
            {
                return _isBusy || _workerActions.Count > 0;
            }
        }
    }

    public CancellationToken CancellationToken => _cancellationTokenSource.Token;

#endregion

#region Private

    private void RunThread()
    {
        (Action Action, string Message)? action = null;

        GoToIdle(false);
        while (_running)
        {
            try
            {
                action = GetNextAction();
                if (action == null)
                {
                    GoToIdle(true);

                    _event.WaitOne();

                    GoToIdle(false);
                }
                else
                {
                    ExecuteAction(action.Value);
                }
            }
            catch (ThreadAbortException e)
            {
                if (_isBusy)
                {
                    Log.Instance.Error($"Error executing {action?.Message}", e);
                }

                _running = false;
            }
            catch (Exception e)
            {
                Log.Instance.Error($"Error executing {action?.Message}", e);
            }
        }

        if (_init)
        {
            OnDeInitThread?.Invoke();
            _init = false;
        }
    }

    private void ExecuteAction((Action Action, string Message) action)
    {
        try
        {
            if (!_init)
            {
                OnInitThread?.Invoke();
                _init = true;
            }

            action.Action();
        }
        catch (OperationCanceledException)
        {
            Log.Instance.Info($"Thread '{_thread.Name}' canceled");
        }
        catch (Exception e)
        {
            Log.Instance.Error($"Thread '{_thread.Name}' failed.", e);
        }
    }

    private void GoToIdle(bool idle)
    {
        _isBusy = !idle;
        IdleEvent?.Invoke(idle);
    }

    private (Action Action, string Message)? GetNextAction()
    {
        using (_semaphoreSlim.Lock())
        {
            if (_workerActions.Count > 0)
            {
                return _workerActions.Dequeue();
            }
        }

        return null;
    }

    private bool _running;
    private bool _init;
    private readonly System.Threading.Thread _thread;
    private readonly SemaphoreSlim _semaphoreSlim = new(1);
    private readonly EventWaitHandle _event = new(false, EventResetMode.AutoReset);
    private readonly Queue _workerActions = new();
    private readonly CancellationTokenSource _cancellationTokenSource = new();
    private bool _isBusy;

#endregion
}

Web Proxy Viewer  |  New URL  |  Original Page