在 Semaphore WaitOne 之后调用 Interlocked
calling Interlocked after Semaphore WaitOne
遇到以下代码,当同时调用 GenerateLabel 超过 4 次时,该代码会阻塞信号量。在 WaitOne 之后,成员 mCurrentScanner 用于访问扫描仪。问题是 WaitOne 之后是否需要联锁功能?我会说不,因为当释放 WaitHandle 时线程重新开始,但不是 100% 确定。
mConcurrentLabels = new Semaphore(4, 4);
public string GenerateLabel()
{
mConcurrentLabels.WaitOne();
int current = 0;
Interlocked.Exchange(ref current, mCurrentScanner);
(scanner, dir) = ScanMappings[current];
Interlocked.Increment(ref mCurrentScanner);
mCurrentScanner %= 4;
DoLongRunningTask();
mConcurrentLabels.Release();
}
就像你说的;信号量用于限制并发线程。但是正文仍然是并发执行的。所以需要locks/interlocked。
更大的问题是:使用 Interlocked.Exchange(ref current, mCurrentScanner);
安全地读取值并使用 Interlocked.Increment(ref mCurrentScanner);
.
可能并发读取相同的值 Exchange()
并将其递增两次。因此,您将 select 一个值两次并跳过下一个值。
我还建议在使用信号量时使用 try/finallies。
mConcurrentLabels = new Semaphore(4, 4);
public string GenerateLabel()
{
mConcurrentLabels.WaitOne();
try
{
int current = Interlocked.Increment(ref mCurrentScanner);
(scanner, dir) = ScanMappings[current];
// mCurrentScanner %= 4; <------ ?
DoLongRunningTask();
}
finally
{
mConcurrentLabels.Release();
}
}
但如果您需要 mod mCurrentScanner,我不会使用 Interlocked。
mConcurrentLabels = new Semaphore(4, 4);
object mSyncRoot = new object();
public string GenerateLabel()
{
mConcurrentLabels.WaitOne();
try
{
int current;
lock(mSyncRoot)
{
current = mCurrentScanner++;
mCurrentScanner %= 4;
}
(scanner, dir) = ScanMappings[current];
// mCurrentScanner %= 4; <------ ?
DoLongRunningTask();
}
finally
{
mConcurrentLabels.Release();
}
}
信号量的目的似乎是保护长运行ning 任务而不是保护对私有变量的访问。
从资源管理的角度来看,这很有用。例如,为了防止太多并发的长 运行ning 任务破坏数据库等共享资源。
需要互锁语句来保护私有变量,因为信号量允许此代码在不同线程上同时 运行 最多四次。
最好将此代码的主要部分放在 try {} finally{}
块中,以保证每次调用 mConcurrentLabels.WaitOne()
时恰好调用一次 mConcurrentLabels.Release()
。
遇到以下代码,当同时调用 GenerateLabel 超过 4 次时,该代码会阻塞信号量。在 WaitOne 之后,成员 mCurrentScanner 用于访问扫描仪。问题是 WaitOne 之后是否需要联锁功能?我会说不,因为当释放 WaitHandle 时线程重新开始,但不是 100% 确定。
mConcurrentLabels = new Semaphore(4, 4);
public string GenerateLabel()
{
mConcurrentLabels.WaitOne();
int current = 0;
Interlocked.Exchange(ref current, mCurrentScanner);
(scanner, dir) = ScanMappings[current];
Interlocked.Increment(ref mCurrentScanner);
mCurrentScanner %= 4;
DoLongRunningTask();
mConcurrentLabels.Release();
}
就像你说的;信号量用于限制并发线程。但是正文仍然是并发执行的。所以需要locks/interlocked。
更大的问题是:使用 Interlocked.Exchange(ref current, mCurrentScanner);
安全地读取值并使用 Interlocked.Increment(ref mCurrentScanner);
.
可能并发读取相同的值 Exchange()
并将其递增两次。因此,您将 select 一个值两次并跳过下一个值。
我还建议在使用信号量时使用 try/finallies。
mConcurrentLabels = new Semaphore(4, 4);
public string GenerateLabel()
{
mConcurrentLabels.WaitOne();
try
{
int current = Interlocked.Increment(ref mCurrentScanner);
(scanner, dir) = ScanMappings[current];
// mCurrentScanner %= 4; <------ ?
DoLongRunningTask();
}
finally
{
mConcurrentLabels.Release();
}
}
但如果您需要 mod mCurrentScanner,我不会使用 Interlocked。
mConcurrentLabels = new Semaphore(4, 4);
object mSyncRoot = new object();
public string GenerateLabel()
{
mConcurrentLabels.WaitOne();
try
{
int current;
lock(mSyncRoot)
{
current = mCurrentScanner++;
mCurrentScanner %= 4;
}
(scanner, dir) = ScanMappings[current];
// mCurrentScanner %= 4; <------ ?
DoLongRunningTask();
}
finally
{
mConcurrentLabels.Release();
}
}
信号量的目的似乎是保护长运行ning 任务而不是保护对私有变量的访问。 从资源管理的角度来看,这很有用。例如,为了防止太多并发的长 运行ning 任务破坏数据库等共享资源。
需要互锁语句来保护私有变量,因为信号量允许此代码在不同线程上同时 运行 最多四次。
最好将此代码的主要部分放在 try {} finally{}
块中,以保证每次调用 mConcurrentLabels.WaitOne()
时恰好调用一次 mConcurrentLabels.Release()
。