最初に入力したスレッドは、同じメソッドの終了を他の同時スレッドに通知できますか?最初に入力されたスレッドは、同じメソッドの終了を他の同時スレッドに通知できますか?
私は、PollDPRAM()という名前のメソッドを持っています。遅いハードウェアへのネットワーク経由でのトリップを行い、オブジェクトのプライベートデータを更新する必要があります。同じメソッドが他のスレッドから同時に呼び出された場合、データは新鮮であるため、最初のスレッドが完了して終了するまで待機してはいけません(10-30ミリ秒前に違いはありません) 。 2番目、3番目などのスレッドが最初に入力されないメソッドで検出が簡単です。 Interlockedカウンタを使用して並行性を検出します。
問題:n> 1スレッドの入口で検出された値よりも小さい値へのカウンタが減少した後に、カウンタ(Interlocked.Read)が監視するように見て、最初のスレッドの終了を検出できませんでした。選択が悪いのは、最初のスレッドが、メソッドが終了した直後に再度メソッドに再入力できるためです。したがって、n> 1のスレッドは決してカウンタでディップを検出しません。
質問: この最初のスレッドが即座に再び入力できる場合でも、最初に入力されたスレッドを正しく検出する方法はありますか?
ありがとうございました
P.S.コード
private void pollMotorsData()
{
// Execute single poll with "foreground" handshaking
DateTime start = DateTime.Now;
byte retryCount = 0;
// Pick old data atomically to detect change
uint motorsDataTimeStampPrev = this.MotorsDataTimeStamp;
bool changeDetected = false;
// The design goal of DPRAM is to ease the bottleneck
// Here is a sensor if bottleneck is actually that tight
long parallelThreads = Interlocked.Increment(ref this.motorsPollThreadCount);
try
{
// For first thread entering the counter will be 1
if (parallelThreads <= 1)
{
do
{
// Handshake signal to DPRAM write process on controller side that host PC is reading
this.controller.deltaTauTcpClient.Pmac_SetBit(OFFSET_0x006A_BIT15_FOREGROUND_READ, 15, true);
try
{
bool canReadMotors = false;
byte[] canReadFrozenDataFlag = new byte[2];
do
{
this.controller.deltaTauTcpClient.Pmac_GetMem(OFFSET_0x006E_BIT15_FOREGROUND_DONE, canReadFrozenDataFlag);
canReadMotors = (canReadFrozenDataFlag[1] & 0x80) == 0x80;
if (canReadMotors) break;
retryCount++;
Thread.Sleep(1);
} while (retryCount < 10);
if (!canReadMotors)
{
throw new DeltaTauControllerException(this.controller, "Timeout waiting on DPRAM Foreground Handshaking Bit");
}
// The lock is meaningless in contructor as it is certainly single threaded
// but for practice sake the access to data should always be serialized
lock (motorsDataLock)
{
// Obtain fresh content of DPRAM
this.controller.deltaTauTcpClient.Pmac_GetMem(OFFSET_0x006A_394BYTES_8MOTORS_DATA, this.motorsData);
this.motorsDataBorn = DateTime.Now;
}
}
finally
{
// Handshake signal to DPRAM write process on controller side that host PC has finished reading
this.controller.deltaTauTcpClient.Pmac_SetBit(OFFSET_0x006A_BIT15_FOREGROUND_READ, 15, false);
}
// Check live change in a separate atom
changeDetected = this.MotorsDataTimeStamp != motorsDataTimeStampPrev;
} while ((!changeDetected) && ((DateTime.Now - start).TotalMilliseconds < 255));
// Assert that result is live
if (!changeDetected)
{
throw new DeltaTauControllerException(this.controller, "DPRAM Background Data timestamp is not updated. DPRAM forground handshaking failed.");
}
}
else
{
// OK. Bottleneck ! The concurrent polls have collided
// Give the controller a breathe by waiting for other thread do the job
// Avoid aggressive polling of stale data, which is not able to be written, locked by reader
// Just wait for other thread do whole polling job and return with no action
// because the data is milliseconds fresh
do
{
// Amount of parallel threads must eventually decrease
// But no thread will leave and decrease the counter until job is done
if (Interlocked.Read(ref this.motorsPollThreadCount) < parallelThreads)
{
// Return is possible because decreased value of concurrentThreads means that
// this very time other thread has finished the poll 1 millisecond ago at most
return;
}
Thread.Sleep(1);
retryCount++;
} while ((DateTime.Now - start).TotalMilliseconds < 255);
throw new DeltaTauControllerException(this.controller, "Timeout 255ms waiting on concurrent thread to complete DPRAM polling");
}
}
finally
{
// Signal to other threads that work is done
Interlocked.Decrement(ref this.motorsPollThreadCount);
// Trace the timing and bottleneck situations
TimeSpan duration = DateTime.Now - start;
if (duration.TotalMilliseconds > 50 || parallelThreads > 1 || retryCount > 0)
{
Trace.WriteLine(string.Format("Controller {0}, DPRAM poll {1:0} ms, threads {2}, retries {3}",
this.controller.number,
duration.TotalMilliseconds,
parallelThreads,
retryCount));
}
}
}
終了するだけでは機能しません。ポーリング方法は、あらゆる年齢のデータに依存しなければならないすべての実際のデータ使用方法の前にあるためです。 15msまたはyongerと言ってください –
ああ、最初のスレッドが実行されていたかどうか気にするだけでなく、データが一定の年齢だった場合も気にしないでください。最後に、変数を必要とします(同期アクセスを使用して)。 – SRM
ありがとうございます。年齢チェック付きの単純なロックが私のために働いた。申し訳ありませんが金曜日の夜のビルドだった –