1

我需要一个行为与AutoResetEvent类完全相同的同步类,但有一个小例外:

Set()方法的调用必须释放所有等待的线程,而不仅仅是一个。

我怎样才能构建这样一个类?我只是没有想法?

  • 马丁。
4

5 回答 5

1

所以你有多个线程在执行 .WaitOne() 并且你想释放它们?

使用 ManualResetEvent 类,所有等待的线程都应该释放......

于 2014-12-12T21:31:22.873 回答
1

非常感谢您的所有想法和意见,我非常感兴趣地阅读了这些内容。我在 Stackoverflow 上进行了更多搜索,突然间我发现了这个,结果证明这正是我想要的。通过将其缩减为我需要的两种方法,我最终得到了这个小班:

public sealed class Signaller
{
    public void PulseAll()
    {
        lock (_lock)
        {
            Monitor.PulseAll(_lock);
        }
    }

    public bool Wait(TimeSpan maxWaitTime)
    {
        lock (_lock)
        {
            return Monitor.Wait(_lock, maxWaitTime);
        }
    }

    private readonly object _lock = new object();
}

它完全符合它的要求!我很惊讶解决方案可以这么简单,我喜欢这样的简单。很美丽。谢谢你,马修·沃森!

  • 马丁。
于 2014-12-14T09:31:37.893 回答
0

你可以尝试两件事。

使用 Barrier 对象也添加有条件的线程并发出信号。

另一种可能是使用 RX 中的发布者订阅者设置。每个线程等待它传递给集合的对象。当您想在每个成员上调用 set 的快照时调用“set”循环。

或者你可以试试熊。

于 2014-12-13T01:45:50.667 回答
0

如果事件被公共字段或属性中的所有线程引用,您可以用新的非信号事件替换公共字段或属性,然后向旧事件发出信号。它有一些成本,因为您将定期创建新的同步对象,但它会起作用。这是我将如何做到这一点的示例:

public static class Example
{
    private static volatile bool stopRunning;
    private static ReleasingAutoResetEvent myEvent;

    public static void RunExample()
    {
        using (Example.myEvent = new ReleasingAutoResetEvent())
        {
            WaitCallback work = new WaitCallback(WaitThread);

            for (int i = 0; i < 5; ++i)
            {
                ThreadPool.QueueUserWorkItem(work, i.ToString());
            }

            Thread.Sleep(500);

            for (int i = 0; i < 3; ++i)
            {
                Example.myEvent.Set();
                Thread.Sleep(5000);
            }

            Example.stopRunning = true;
            Example.myEvent.Set();
        }
    }

    private static void WaitThread(object state)
    {
        while (!Example.stopRunning)
        {
            Example.myEvent.WaitOne();
            Console.WriteLine("Thread {0} is released!", state);
        }
    }
}

public sealed class ReleasingAutoResetEvent : IDisposable
{
    private volatile ManualResetEvent manualResetEvent = new ManualResetEvent(false);

    public void Set()
    {
        ManualResetEvent eventToSet = this.manualResetEvent;
        this.manualResetEvent = new ManualResetEvent(false);
        eventToSet.Set();
        eventToSet.Dispose();
    }

    public bool WaitOne()
    {
        return this.manualResetEvent.WaitOne();
    }

    public bool WaitOne(int millisecondsTimeout)
    {
        return this.manualResetEvent.WaitOne(millisecondsTimeout);
    }

    public bool WaitOne(TimeSpan timeout)
    {
        return this.manualResetEvent.WaitOne(timeout);
    }

    public void Dispose()
    {
        this.manualResetEvent.Dispose();
    }
}
于 2014-12-13T19:22:29.693 回答
0

您可以尝试使用 Monitor 类锁定和解锁对象的另一个更轻量级的解决方案如下。但是,我对这个版本的 ReleasingAutoResetEvent 的清理故事并不满意,因为 Monitor 可能持有对它的引用,并且如果没有正确处理,它会无限期地保持活动状态。

这个实现有一些限制/陷阱。首先,创建该对象的线程将是唯一能够通过调用 Set 来发出信号的线程;尝试执行相同操作的其他线程将收到 SynchronizationLockException。其次,创建它的线程将永远无法成功等待它,因为它已经拥有锁。如果您只有一个控制线程和几个其他等待线程,这将是一个有效的解决方案。

public static class Example
{
    private static volatile bool stopRunning;
    private static ReleasingAutoResetEvent myEvent;

    public static void RunExample()
    {
        using (Example.myEvent = new ReleasingAutoResetEvent())
        {
            WaitCallback work = new WaitCallback(WaitThread);

            for (int i = 0; i < 5; ++i)
            {
                ThreadPool.QueueUserWorkItem(work, i.ToString());
            }

            Thread.Sleep(500);

            for (int i = 0; i < 3; ++i)
            {
                Example.myEvent.Set();
                Thread.Sleep(5000);
            }

            Example.stopRunning = true;
            Example.myEvent.Set();
        }
    }

    private static void WaitThread(object state)
    {
        while (!Example.stopRunning)
        {
            Example.myEvent.WaitOne();
            Console.WriteLine("Thread {0} is released!", state);
        }
    }
}

public sealed class ReleasingAutoResetEvent : IDisposable
{
    private volatile object lockObject = new object();

    public ReleasingAutoResetEvent()
    {
        Monitor.Enter(this.lockObject);
    }

    public void Set()
    {
        object objectToSignal = this.lockObject;
        object objectToLock = new object();

        Monitor.Enter(objectToLock);
        this.lockObject = objectToLock;
        Monitor.Exit(objectToSignal);
    }

    public void WaitOne()
    {
        object objectToMonitor = this.lockObject;
        Monitor.Enter(objectToMonitor);
        Monitor.Exit(objectToMonitor);
    }

    public bool WaitOne(int millisecondsTimeout)
    {
        object objectToMonitor = this.lockObject;
        bool succeeded = Monitor.TryEnter(objectToMonitor, millisecondsTimeout);

        if (succeeded)
        {
            Monitor.Exit(objectToMonitor);
        }

        return succeeded;
    }

    public bool WaitOne(TimeSpan timeout)
    {
        object objectToMonitor = this.lockObject;
        bool succeeded = Monitor.TryEnter(objectToMonitor, timeout);

        if (succeeded)
        {
            Monitor.Exit(objectToMonitor);
        }

        return succeeded;
    }

    public void Dispose()
    {
        Monitor.Exit(this.lockObject);
    }
}
于 2014-12-13T20:05:29.097 回答