C# signal between threads

Date: 2020-06-16
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace Domain.Helpers
{
    public class SignalWaiter : IDisposable
    {
        public delegate void SignalHandler();
        private static readonly SynchronizedCollection<KeyValuePair<string, Semaphore>> Handlers = new SynchronizedCollection<KeyValuePair<string, Semaphore>>();

        private readonly KeyValuePair<string, Semaphore> _pair;
        private readonly CancellationToken _cancellationToken;
        private readonly TimeSpan _timeout;

        public static void SendSignal(string key)
        {
            Handlers.Where(x => x.Key == key).Select(x => x.Value).ToList().ForEach(x => x.Release());
        }

        public SignalWaiter(string key) : this(key, TimeSpan.FromMinutes(1), new CancellationToken())
        {
        }
        public SignalWaiter(string key, TimeSpan timeout, CancellationToken cancellationToken)
        {
            _pair = new KeyValuePair<string, Semaphore>(key, new Semaphore(0, 1));
            _cancellationToken = cancellationToken;
            _timeout = timeout;
            Handlers.Add(_pair);
        }

        public async Task Wait()
        {
            var task1 = Task.Run(() => _cancellationToken.WaitHandle.WaitOne());
            var task2 = Task.Run(async () => await _pair.Value.WaitAsync(_timeout)); // _cancellationToken meegeven werkt niet afdoende
            await Task.WhenAny(task1, task2);
        }

        #region IDisposable Support
        private bool disposedValue = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    Handlers.Remove(_pair);
                    _pair.Value.Dispose();
                }
                disposedValue = true;
            }
        }
        public void Dispose()
        {
            Dispose(true);
        }
        #endregion
    }
}




// example usage
var cancellationToken = Request.GetOwinContext()?.Request?.CallCancelled ?? default(CancellationToken);
Task.Delay(15000).ContinueWith(x => SignalWaiter.SendSignal("signal1key"));
Console.WriteLine($"{DateTime.Now:o} starting");
using (var signalWaiter = new SignalWaiter("signal1key", TimeSpan.FromSeconds(60), cancellationToken))
{
        await signalWaiter.Wait();
	Console.WriteLine($"{DateTime.Now:o} signaled!");
}
Console.WriteLine($"{DateTime.Now:o} finished!");
Console.ReadKey();
37760cookie-checkC# signal between threads