如何允许所有后续线程继续通过“门”

时间:2018-11-13 08:52:50

标签: c# concurrency

我需要能够让一个信号量的线程等待,但是当我释放它们时,没有其他线程应该再次对该信号量等待-它应该只允许任何后续线程。我找不到任何示例。

这里是一个例子。设置后,我需要允许所有对“ Result”属性的读取(不仅仅是允许一次读取)。

private class TaskResultWrapper<T>
    {
        private T result;

        private SemaphoreSlim valueSetSemaphore = new SemaphoreSlim(0, 1);
        private CancellationToken cancellationToken;

        public TaskResultWrapper(CancellationToken cancellationToken)
        {
            this.cancellationToken = cancellationToken;
        }

        public T Result
        {
            get {
                valueSetSemaphore.Wait(cancellationToken);
                return result;
            }

            set
            {
                result = value;
                valueSetSemaphore.Release();
            }
        }
    }

2 个答案:

答案 0 :(得分:3)

我建议您使用ManualResetEvent。它就是为此而设计的。 在手动重置事件实例上调用WaitOne()的所有线程将被阻塞,直到通过调用Set()发出手动重置事件的信号为止。然后,释放所有阻塞的线程。

答案 1 :(得分:1)

这听起来像是您用TaskResultWrapper在这里重新发明了轮子。 看来您实际上可以使用TaskCompletionSource来做到这一点。

请注意,这也支持取消。这是一个示例:

using System;
using System.Threading.Tasks;

namespace ConsoleApp10
{
    class Program
    {
        static void Main()
        {
            var test = new TaskCompletionSource<int>();

            Task.Run(() => Parallel.Invoke(
                () => printValue(test.Task),
                () => printValue(test.Task),
                () => printValue(test.Task)));

            Console.WriteLine("Tasks are all waiting on the value; press return to continue.");
            Console.ReadLine();

            test.SetResult(42); // Or test.SetCanceled() to cancel it.

            Console.WriteLine("Set result to 42 (or cancelled)");
            Console.ReadLine();
        }

        static void printValue(Task<int> task)
        {
            try
            {
                Console.WriteLine(task.Result);
            }

            catch (Exception exception)
            {
                Console.WriteLine("Task received exception: " + exception.InnerException.Message);
            }
        }
    }
}

尝试按原样运行,然后将test.SetResult(42)更改为test.SetCanceled()