我目前正在构建一个多线程软件(在C#中),我不确定我的问题解决方案。
// isLocked is initialized at earlier stage
if (!isLocked)
{
isLocked = true;
// More code here
}
我知道条件检查是原子的,但我认为另一个线程可能会在' isLocked'之前进入if块。被分配了真实的'价值(因此造成不必要的情况)。
在Java中,我可以使用AtomicBoolean的方法' compareAndSet'这是原子的,但C#是等价的' CompareExchange'不是atmoic。
除了bool
之外,我尝试使用lock
,这样如果"已锁定"代码已经被执行,其他线程将绕过它。这是一个很好的方法吗,还是有更好的方法?
Object myLock = new object();
bool free = false;
bool isLocked= actorsLocks[i];// Some Data structure
if (!isLocked)
{
lock(mylock)
{
if (!isLocked)
{
isLocked= true;
free = true;
}
}
}
if(free)
{
// actual method code here...
}
是否有更有效的解决方案?
非常感谢你。
答案 0 :(得分:1)
C#所需要的只是:
object theLock = new Object();
lock(theLock)
{
// Lock is yours, enjoy
}
答案 1 :(得分:1)
在Java中,我可以使用AtomicBoolean的方法' compareAndSet'是的 原子,但 C#等价' CompareExchange'不是atmoic。
......呃......是的。否则它将完全没用。
https://msdn.microsoft.com/en-us/library/801kt583(v=vs.110).aspx#Anchor_2
如果comparand和location1中的值相等,则值为 存储在location1中。否则,不执行任何操作。 比较 和交换操作是作为原子操作执行的。 CompareExchange的返回值是location1中的原始值, 是否进行交换。
你的解决方案很接近。确保所有线程都可以访问您的锁定对象及其保护的所有变量。锁定本地声明的对象对你没有任何好处。例如,myLock
可以是类数据成员。绝对不应该在函数中声明。同样适用于isLocked
。
class C
{
int[] m_actorsLocks; // *See below
...
void WorkerFunction(int threadIndex)
{
if (CompareExchange(ref m_actorsLocks[threadIndex], 1, 0) == 0) // cmpxchg returns old value. If old value was false, it means WE locked it!
{
// do work
// use cmpxchg to free the lock
CompareExchange(ref m_actorsLocks[threadIndex], 0, 1)
// I do this simply because I don't understand how C# caches outgoing writes
// could possibly do m_actorsLocks[threadIndex] = 0; surrounded by Interlocked.MemoryBarrier()
}
else
{
// Threads who didn't get the lock come here...
// If I understand what you're trying to do, you don't want the other threads to wait if they didn't get the lock.
// So you probably wouldn't need this else clause...
}
}
...
};
*如果将锁存储在数组中,您将会遇到错误的共享。由于数组是连续存储的,因此您的锁将位于同一个缓存行中...您将拥有与为所有内容使用1个锁相同的性能。处理这个的蹩脚方法是填充你的阵列。例如:
// If I want 4 locks
int[] m_actorsLocks = new int[32];
// Now i only use index 0, 8, 16, and 24. The rest are just padding.
它有点混乱,需要知道你的架构...最好对这个进行一些研究,也许可以提出一个单独的问题。
答案 2 :(得分:0)
你是对的:bool的读取是原子的,但是几个线程可以原子地读取 bool并输入' if'在标志变为假之前阻止。 要做你想做的事情(一个线程只输入' if'块而不阻塞其他线程)你可以使用以下类型:
private long _n = 0;
......
if (Interlocked.Exchange(ref _n, 1) == 0)
{
// More code here, only one thread at a time.
// Be carefull with exceptions.
Interlocked.Exchange(ref _n, 0); // Reset the flag for next running.
}
Interlocked.Exchange是一个原子读取和写入:它将阻止所有线程,除了将读取' _n = 0'写' _n = 1' 作为原子操作。在互锁的法规返回后,其他线程将立即获得1,并且它们不会进入块。
答案 3 :(得分:0)
如果您想同时尝试获取锁定并找出是否已获得锁定,请在一次原子操作中使用Monitor.TryEnter(object)
。
Monitor.TryEnter
返回true,否则返回false。如果TryEnter
返回true,则仅执行“锁定”代码。
public class SomeClassThatMultipleThreadsAccess
{
private readonly object _lockObject = new object();
public void MethodThatGetsCalledConcurrently()
{
if(Monitor.TryEnter(_lockObject))
{
try
{
// only one thread at a time can execute this in
// one instance of the class.
// If _lockObject is static then only one thread at
// a time can execute this across all instances of
// the class.
}
finally // very important - if we don't exit then nothing else can enter.
{
Monitor.Exit(_lockObject);
}
}
}
}
请注意,用于锁定的对象不能在使用它的同一范围内声明。
这些都无济于事:
var lockObject = new object();
if(Monitor.TryEnter(lockObject))
var lockObject = new object();
lock(lockObject)
{
因为每个线程都会创建一个不同的对象,所以每个线程都会立即获取锁。它不会阻止对任何事情的并发访问。必须有一个对象,多个线程尝试获取锁。
答案 4 :(得分:0)
简而言之:你在寻找麻烦。别这么做。
更详细地说,有许多因素你低估了:
这就是可以直接命名的东西。如果想得更多,我们可以找到更多针对手工线程同步机制的缺点。因此,多线程就像加密一样:不要试图重新发明它,你在危险的情况下知之甚少,并且会在几分钟甚至几秒钟内被破解。