简体   繁体   English

如何制作稍微修改的AutoResetEvent类?

[英]How to make a slightly modified AutoResetEvent class?

I need a synchronizing class that behaves exactly like the AutoResetEvent class, but with one minor exception: 我需要一个行为类与AutoResetEvent类完全相同的同步类,但有一个小例外:

A call to the Set() method must release all waiting threads, and not just one. 调用Set()方法必须释放所有等待的线程,而不仅仅是一个。

How can I construct such a class? 我该如何构造这样的课程? I am simply out of ideas? 我只是没有主意吗?

  • Martin. 马丁。

So you have multiple threads doing a .WaitOne() and you want to release them? 因此,您有多个线程在执行.WaitOne()并想释放它们?

Use the ManualResetEvent class and all the waiting threads should release... 使用ManualResetEvent类,所有等待线程都应释放...

Thank you very much for all your thougts and inputs which I have read with great interest. 非常感谢您阅读我的所有意见和建议,我非常感兴趣。 I did some more searching here on Stackoverflow, and suddenly I found this , whcih turned out to be just what I was looking for. 我在这里在Stackoverflow上进行了更多搜索,突然间我发现正是我在寻找的东西。 By cutting it down to just the two methods I need, I ended up with this small class: 通过将其缩减为仅需要的两种方法,我得到了这个小类:

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();
}

and it does excactly what it should! 它确实确实应有的功能! I'm amazed that a solution could be that simple, and I love such simplicity. 我惊讶的是,解决方案如此简单,我喜欢这种简单性。 I'ts beautiful. 很美丽。 Thank you, Matthew Watson! 谢谢Matthew Watson!

  • Martin. 马丁。

Two things you might try. 您可以尝试两件事。

Using a Barrier object add conditionally adding threads too it and signaling them. 使用Barrier对象也可以有条件地添加线程,并向它们发出信号。

The other might be to use a publisher subscriber setup like in RX. 另一种可能是像在RX中那样使用发布者订户设置。 Each thread waits on an object that it passes to a collection. 每个线程都在等待传递给集合的对象。 When you want to call 'set' loop over a snapshot of it calling set on each member. 当您想调用“ set”时,将在其快照上循环调用每个成员上的set。

Or you could try bears. 或者您可以尝试熊。

If the event is being referenced by all threads in a common field or property, you could replace the common field or property with a new non-signaled event and then signal the old one. 如果该事件被公共字段或属性中的所有线程引用,则可以用新的非信号事件替换该公共字段或属性,然后用信号通知旧事件。 It has some cost to it since you'll be regularly creating new synchronization objects, but it would work. 由于您将定期创建新的同步对象,因此会付出一定的代价,但是它会起作用。 Here's an example of how I would do that: 这是我将如何执行此操作的示例:

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();
    }
}

Another more lightweight solution you could try that uses the Monitor class to lock and unlock objects is below. 您可以尝试使用Monitor类来锁定和解锁对象的另一个更轻量的解决方案如下。 However, I'm not as happy with the cleanup story for this version of ReleasingAutoResetEvent since Monitor may hold a reference to it and keep it alive indefinitely if it is not properly disposed. 但是,对于此版本的ReleasingAutoResetEvent的清理故事,我不太满意,因为Monitor可能会保留对该引用的引用,如果未正确处理,则可以无限期地保持它的生命。

There are a few limitations/gotchas with this implementation. 此实现存在一些限制/陷阱。 First, the thread that creates this object will be the only one that will be able to signal it with a call to Set; 首先,创建该对象的线程将是唯一能够通过调用Set来发出信号的线程。 other threads that attempt to do the same thing will receive a SynchronizationLockException. 其他尝试执行相同操作的线程将收到SynchronizationLockException。 Second, the thread that created it will never be able to wait on it successfully since it already owns the lock. 其次,创建它的线程将永远无法成功等待它,因为它已经拥有了锁。 This will only be an effective solution if you have exactly one controlling thread and several other waiting threads. 仅当您只有一个控制线程和几个其他等待线程时,这才是有效的解决方案。

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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM