我正在尝试了解如何在使用线程时使用信号量。
我有两个使用相同资源的线程 - 一个Arraylist。 一种方法将随机温度添加到列表中,另一种方法计算列表的平均温度。
如何在此上下文中使用信号量属性Wait和Release? 以及如何控制在我的列表中添加某些内容之后开始计算平均温度的线程。
这是我的一些代码:
class Temperature
{
private static Random random = new Random();
private static ArrayList buffer = new ArrayList();
static SemaphoreSlim e, b;
public static void Main (string[] args)
{
e = new SemaphoreSlim(6); //how will this work?
b = new SemaphoreSlim(1);
Thread t1 = new Thread (Add);
t1.Start ();
Thread t2 = new Thread (Average);
t2.Start ();
}
public static void Add()
{
int temperature;
for (int i=0; i<50; i++)
{
temperature = random.Next (36, 42);
Console.WriteLine ("Temperature added to buffer: " + temperature);
b.Wait ();
e.Wait ();
buffer.Add(temperature);
b.Release ();
Thread.Sleep (50);
}
答案 0 :(得分:2)
仅使用一个信号量:
var b = new SemaphoreSlim(1); // 1 -> allow 1 thread to enter the critical section at one time.
然后用它来保护你的缓冲区(=关键部分)
b.Wait ();
buffer.Add(temperature);
b.Release ();
如果你不介意使用锁,请改用:
private readonly object _locker_ = new object();
然后将其与lock
语句一起使用:
lock(_locker_)
{
buffer.Add(temperature);
}
不要忘记在Average()
方法中执行相同操作。
答案 1 :(得分:2)
你需要两件事。
您可以使用信号量执行该操作,但锁定或Monitor更合适。另一个真的需要一个事件,可能是AutoResetEvent
。
private static Random random = new Random();
private static ArrayList buffer = new ArrayList();
static SemaphoreSlim BufferLock = new SemaphoreSlim(1);
static AutoResetEvent ItemAdded = new AutoResetEvent(false);
public static void Main (string[] args)
{
BufferLock.Wait(); // initially acquire the semaphore.
Thread t1 = new Thread (Add);
t1.Start ();
Thread t2 = new Thread (Average);
t2.Start ();
// wait for adding thread to finish
t1.Wait();
}
public static void Add()
{
int temperature;
for (int i=0; i<50; i++)
{
temperature = random.Next (36, 42);
BufferLock.Wait();
buffer.Add(temperature);
ItemAdded.Set();
BufferLock.Release();
Console.WriteLine ("Temperature added to buffer: " + temperature);
Thread.Sleep (50);
}
}
public static void Average()
{
while (true)
{
ItemAdded.Wait(); // wait for item to be added
BufferLock.Wait();
ComputeAverage(); // however you want to do this
BufferLock.Release();
}
}
如果您想确保计算最后一次平均值,则必须等待t2
。当然,您需要一种方法来告诉线程退出。查看Cancellation。
你真的不应该使用ArrayList
。 List< int >将是一个更好的选择。使用ArrayList
的唯一原因是当您为2.0之前的.NET版本编写代码时,或者当您支持非常旧的代码时。
你可以用AutoResetEvent
替换Semaphore
来模拟事件通知,但这样做有点笨拙,需要非常仔细的编码才能正确。我当然不希望这样做。
答案 2 :(得分:2)
正如其他人所指出的,直接锁定比这里的信号量更合适。
之所以这样,是因为您只有一个资源(buffer
)可以防止并发访问。只有一个资源忙或不忙,锁是最合适的。
当资源的容量有限时,信号量很有用。一个例子:
像火焰限制的酒吧或夜总会这样的场所。人(线程)等待直到保镖(信号量)看到场地(资源)具有容量。第一个客户端不必等待,实际上他们会立即进入。
当他们进入保镖( Semaphore )时,点击他的计数器以记录消耗的容量。一旦场地达到容量,就会建立一个队列 - 人们必须等待,直到有人离开。
当人们离开(他们发布)并且保镖( Semaphore )减少了他的计数器 - 相应数量的人现在可以进入。
所以 - 您在信号量上设置容量,资源的消费者等待使其可用(如果容量不可用则会阻止)和消费者发布时他们完成了资源。
一个共同的特征(但不是必要的特征)是当你不知道(或关心)资源的哪个特定部分被消耗时,通常会使用信号量,只是它的一些可用部分被使用
Mutex
类似于lock
,但它是一个命名的系统范围资源。 Mutex
必须由获得它的Thread
释放,而Semaphore
没有线程标识。这可以解释为什么您有时会看到Semaphore
使用的计数为1而不是Mutex
。
答案 3 :(得分:0)
我想你想在Add方法完成它并且在ArrayList对象周围保持线程安全时,让你的Average方法启动。 所以......
var s = new SemaphoreSlim(0);
private object myLock = new object();
Add()
{
Calculate();
lock(myLock)
{
buffer.Add(temperature);
SemaphoreSlim.Release();
}
}
Average()
{
SemaphoreSlim.Wait();
lock(myLock)
{
CalcAvg();
}
}
答案 4 :(得分:0)
虽然您可以使用初始为零Semaphore
来实现所需的信令,但您最好使用AutoResetEvent
。使计算线程在事件句柄上等待,并在添加到列表后触发事件。保持lock
和@ klark的解决方案一样。
有关为作业读取http://msdn.microsoft.com/en-us/library/ms228964(v=vs.110).aspx
选择正确的同步原语的更多信息