在并发调用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();
}发布于 2021-09-07 09:28:15
正如您所说的,信号量用于限制并发线程。但身体仍同时执行。所以锁/联锁是必需的。
更大的问题是:使用Interlocked.Exchange(ref current, mCurrentScanner);安全读取值,使用Interlocked.Increment(ref mCurrentScanner);。
可以并发读取相同的值Exchange()并将其增加两次。因此,您将选择一个值两次,并跳过下一个值。
我还建议在使用信号量时使用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();
}
}但是如果你需要修改mCurrentScanner,我不会使用互锁的。
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();
}
}发布于 2021-09-07 09:32:52
信号量的目的似乎是保护长期运行的任务,而不是保护对私有变量的访问。从资源管理的角度来看,这是非常有用的。例如,为了防止过多并发长时间运行的任务破坏共享资源(如数据库)。
需要使用互锁语句来保护私有变量,因为信号量允许此代码在不同线程上并发运行四次。
很好的做法是将此代码的主要部分放在try {} finally{}块中,以确保每次调用mConcurrentLabels.WaitOne()时都准确地调用mConcurrentLabels.Release()一次。
https://stackoverflow.com/questions/69085229
复制相似问题