我正在阅读以下文章:
http://www.albahari.com/threading
并且我无法意识到AutoResetEvent和用maximumCount=1初始化的信号量之间的区别。只是想看看我是否做对了。。。考虑到这种用法,这两个结构有什么区别吗?
谢谢!
是的,肯定有区别。Semaphore
用于限制对资源或代码块的访问。当调用WaitOne
时,线程将阻塞,直到信号量中的计数可用为止。若要进行计数,请致电Release
。最大计数为1的信号量通常称为二进制信号量。这种性质的信号量只允许从单个线程访问资源或块代码。您可以使用二进制信号量来代替互斥或监视器。关于Semaphore
,需要记住的重要一点是,它的状态是通过调用WaitOne
和Release
手动控制的。
另一方面,CCD_ 7主要用作信令机制。一个线程将通过对WaitOne
的调用进行阻塞,等待信号。另一个线程将调用Set
来启动该信号。ARE将此信号发布到一个且只有一个线程,然后立即将ARE重置为无信号状态。关于AutoResetEvent
,需要记住的重要一点是,它是通过对Set
的调用来手动发出信号的,并且当对WaitOne
的单个调用返回时,会自动重置。
以下是差异总结:
Semaphore
的状态是手动控制的AutoResetEvent
的状态是手动设置的,但会自动重置- 对于
Semaphore
,线程通常平衡Release
和WaitOne
调用 - 对于
AutoResetEvent
,一个线程通常被指定为信号员,另一个线程被指定为服务员 Semaphore
限制对资源或代码块的访问AutoResetEvent
用信号通知线程采取一些操作
把AutoResetEvent
想象成通往走廊的门。在收到命令后,门将允许一个人通过。一旦一个人通过门,门就会立即关闭,等待另一个命令。只要门不断收到新的命令,走廊就可以自由地容纳与发出的命令数量一样多的人。
把Semaphore
想象成通往同一走廊的门。这扇门将允许一定数量的人进入走廊。门一直打开,直到走廊达到其占用限制,此时门关闭。有人从另一边离开走廊后,这扇门又打开了。
更新:
以下是一个最简单的例子,它表明某些东西显然是不同的。
static void Main()
{
var are = new AutoResetEvent(false);
are.Set();
are.Set();
var semaphore = new Semaphore(0, 1);
semaphore.Release();
semaphore.Release();
}
毫不奇怪,您会在第二次semaphore.Release
调用中得到异常,而对Set
的第二次调用通过得很好。原因是ARE正在设置布尔标志,而信号量正试图增加计数。
WaitOne
方法将以相同的方式工作,但Release
和Set
方法不会。正是由于这个原因,二进制信号量不能与ARE互换。然而,在某些情况下,ARE可以与二进制信号交换。
存在重叠的一种情况是在单个线程的锁存器的情况下。
public static void Main()
{
var latch = new AutoResetEvent(false);
new Thread(
() =>
{
latch.WaitOne(); // Wait for the latch.
}).Start();
latch.Set(); // Release the latch.
}
这是一个只有AutoResetEvent
才能满足的场景。
static void Main()
{
var are = new AutoResetEvent(false);
new Thread(
() =>
{
while (true)
{
are.WaitOne();
Console.WriteLine("go");
Thread.Sleep(2000);
}
}).Start();
while (true)
{
are.Set();
Console.WriteLine("pulse");
Thread.Sleep(1000);
}
}
一个AutoResetEvent
,用Joe Albahari的比喻:
就像一个检票口:插入一张票可以让一个人通过
所以它就像一扇单向门。
相比之下,计数为1的Semaphore
就像一个只能容纳一个人的房间,它有一个"已占用"的标志。信号灯用于控制对资源(房间)的访问,计数指定有多少资源(房间可以容纳多少人)。
从用户的角度来看,大多数情况下都是一样的,一个小的区别是AutoResetEvent
上的多个连续信号将成功,这使得对资源生产消耗问题进行建模变得不合适。看看这篇文章-";自动重置事件只是一个愚蠢的信号量;。
最大值为1的信号量就像一个正常的System.Threading.Monitor或锁"如果我们忽略命名系统信号量在整个操作系统中可见的事实…"这提供了一个关键部分等待线程,该线程被阻塞,直到当前线程完成对资源"."的使用,因此只有一个线程可以访问一个资源。
您可以将AutoResetEvent视为通知程序,一个线程将等待,直到收到来自另一个线程的通知才能继续,而另一个稍后将设置为使另一线程再次等待通知"注意,AutoResetEventwill automatically set the waiting thread again"。。。
AutoRestEvent的一个例子是生产者/消费者使用集合,第一个线程将等待并添加元素来消费它,另一个线程"生产者"每当添加元素时都会发出信号"通知"等待通知的线程"被阻止等待通知",然后第一个线程将处理"消费"新添加的元素,然后再次等待,直到添加另一个元素。。
//编辑:如果您已经知道AutoResetEvent
和Semaphore
之间的区别,并询问"作为您的评论"
如果我们限制自己只使用WaitOne和Set进行ARE
如果您只使用_autoResetEvent.WaitOne()
和_autoResetEvent.Set()
,那么您可以获得相同的结果-但您应该小心使用-通过使用_semaphore.WaitOne()
和_semaphore.Release()
:
AutoResetEVent _autoResetEvent = new AutoResetEvent(true);
Semaphore _semaphore = new Semaphore(1, 1);
private void Foo()
{
_autoResetEvent.WaitOne();
try
{
//some code
Console.WriteLine("Thread At Foo Entered {0}", Thread.CurrentThread.ManagedThreadId);
}
finaly
{
_autoResetEvent.Set();
}
}
private void Bar()
{
_semaphore.WaitOne();
try
{
//some code
Console.WriteLine("Thread At Bar Entered {0}", Thread.CurrentThread.ManagedThreadId);
}
finaly
{
_semaphore.Release();
}
}
void Main()
{
new Thread(Foo) { IsBackground = true }.Start();
new Thread(Foo) { IsBackground = true }.Start();
new Thread(Bar) { IsBackground = true }.Start();
new Thread(Bar) { IsBackground = true }.Start();
Thread.Sleep(2000);//give it some time to execute.
Console.ReadLine();
}
//output is something like this:
Thread At Foo Entered 11
Thread At Foo Entered 12
Thread At Far Entered 13
Thread At Far Entered 14
信号量用于控制对关键部分的访问。当您想向其他线程发出某个事件已经发生的信号,以便他们对此做出反应时,会使用Auto/ManualResetEvent——它们实际上与保护进入关键部分无关。
我不确定你指的是哪种特定的用法,但我想你可以使用锁和事件来实现信号量,所以你的问题的答案可能是否定和肯定,这取决于你想要什么。