场景如下:有几个低优先级线程可以被高优先级线程中断。每当高优先级线程要求低优先级线程暂停时,它们将进入Wait
状态(如果它们尚未处于等待状态)。然而,当一个高优先级线程发出低优先级线程可以Resume
的信号时,低优先级线程不应该恢复,直到所有要求低优先级线程暂停的高优先级线程都同意。
为了解决这个问题,我在计数器变量中跟踪Pause()
从高优先级线程到低优先级线程的调用。每当高优先级线程向低优先级线程请求 时Pause()
,计数器的值就会增加 1。如果在增加后计数器的值是1
,则表示线程不处于Wait
,因此要求它进入Wait
状态。否则只是增加counter
价值。相反,当高优先级线程调用时Resume()
,我们将counter
值递减,如果递减后值为0
,则表示低优先级线程Resume
现在可以。
这是我的问题的简化实现。if 语句中的比较操作Interlocked.XXX
不正确,即
if (Interlocked.Increment(ref _remain) == 1)
,因为读取/修改和比较操作不是原子的。
我在这里想念什么?我不想使用线程优先级。
using System;
using System.Collections.Generic;
using System.Threading;
namespace TestConcurrency
{
// I borrowed this class from Joe Duffy's blog and modified it
public class LatchCounter
{
private long _remain;
private EventWaitHandle m_event;
private readonly object _lockObject;
public LatchCounter()
{
_remain = 0;
m_event = new ManualResetEvent(true);
_lockObject = new object();
}
public void Check()
{
if (Interlocked.Read(ref _remain) > 0)
{
m_event.WaitOne();
}
}
public void Increment()
{
lock(_lockObject)
{
if (Interlocked.Increment(ref _remain) == 1)
m_event.Reset();
}
}
public void Decrement()
{
lock(_lockObject)
{
// The last thread to signal also sets the event.
if (Interlocked.Decrement(ref _remain) == 0)
m_event.Set();
}
}
}
public class LowPriorityThreads
{
private List<Thread> _threads;
private LatchCounter _latch;
private int _threadCount = 1;
internal LowPriorityThreads(int threadCount)
{
_threadCount = threadCount;
_threads = new List<Thread>();
for (int i = 0; i < _threadCount; i++)
{
_threads.Add(new Thread(ThreadProc));
}
_latch = new CountdownLatch();
}
public void Start()
{
foreach (Thread t in _threads)
{
t.Start();
}
}
void ThreadProc()
{
while (true)
{
//Do something
Thread.Sleep(Rand.Next());
_latch.Check();
}
}
internal void Pause()
{
_latch.Increment();
}
internal void Resume()
{
_latch.Decrement();
}
}
public class HighPriorityThreads
{
private Thread _thread;
private LowPriorityThreads _lowPriorityThreads;
internal HighPriorityThreads(LowPriorityThreads lowPriorityThreads)
{
_lowPriorityThreads = lowPriorityThreads;
_thread = new Thread(RandomlyInterruptLowPriortyThreads);
}
public void Start()
{
_thread.Start();
}
void RandomlyInterruptLowPriortyThreads()
{
while (true)
{
Thread.Sleep(Rand.Next());
_lowPriorityThreads.Pause();
Thread.Sleep(Rand.Next());
_lowPriorityThreads.Resume();
}
}
}
class Program
{
static void Main(string[] args)
{
LowPriorityThreads lowPriorityThreads = new LowPriorityThreads(3);
HighPriorityThreads highPriorityThreadOne = new HighPriorityThreads(lowPriorityThreads);
HighPriorityThreads highPriorityThreadTwo = new HighPriorityThreads(lowPriorityThreads);
lowPriorityThreads.Start();
highPriorityThreadOne.Start();
highPriorityThreadTwo.Start();
}
}
class Rand
{
internal static int Next()
{
// Guid idea has been borrowed from somewhere on StackOverFlow coz I like it
return new System.Random(Guid.NewGuid().GetHashCode()).Next() % 30000;
}
}