シナリオは次のとおりです。優先度の高いスレッドによって中断される可能性のある優先度の低いスレッドがいくつかあります。優先度の高いスレッドが優先度の低いスレッドに一時停止を要求するたびに、それらは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;
}
}