我需要一个与 AutoResetEvent 类完全相同的同步类,但只有一个小例外:
对 Set()方法的调用必须释放所有等待线程,而不只是一个。
我怎样才能构建这样一个类?我只是出于想法?
答案 0 :(得分:1)
所以你有多个线程在做.WaitOne()而你想发布它们吗?
使用ManualResetEvent类,所有等待的线程都应该释放...
答案 1 :(得分:1)
非常感谢你所有我非常感兴趣的阅读和投入。我在Stackoverflow上做了一些搜索,突然我发现了this,结果证明这正是我想要的。通过将其简化为我需要的两种方法,我最终得到了这个小班:
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();
}
它确实应该是什么!我很惊讶解决方案可能就这么简单,我喜欢这种简单。我很漂亮。谢谢Matthew Watson!
答案 2 :(得分:0)
你可能会尝试两件事。
使用Barrier对象添加有条件地添加线程并发信号通知。
另一种可能是在RX中使用发布者订阅者设置。每个线程等待它传递给集合的对象。当你想在每个成员上调用set的快照时调用'set'循环。
或者你可以尝试熊。
答案 3 :(得分: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();
}
}
答案 4 :(得分: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);
}
}