[英]Overuse of Interlocked.exchange?
I am trying to understand correct usage of Interlocked.Exchange, so I am implementing a simple sorted LinkedList with add and remove functionality. 我试图了解Interlocked.Exchange的正确用法,所以我正在实现具有添加和删除功能的简单排序的LinkedList。
If this was not a threadsafe list, obviously to find the insertion point, you'd have something like the below to find the correct point to insert then new node. 如果这不是线程安全列表,显然是要找到插入点,您将具有以下类似内容,以找到要插入的正确点,然后再插入新节点。
public void Insert(int newValue)
{
var prev = _header;
Node curr = _header.Next;
while(curr != null && curr.value > newValue )
{
prev = curr;
curr = curr.Next;
}
var newNode = new Node(newValue, curr);
prev.Next = newNode;
}
Below is my take on how you'd have to do this for a concurrent list. 以下是我对如何为并发列表执行此操作的看法。 Is there too much Interlocked.Exchange's going on? Interlocked.Exchange正在进行太多吗? Without this, would the insert still be threadsafe? 没有这个,插入是否仍然是线程安全的? Would hundreds or thousands of Interlocked operations cause bad performance? 数百或数千个互锁操作会导致性能下降吗?
public void InsertAsync(int newValue)
{
var prev = _header;
Node curr = new Node(0, null);
Interlocked.Exchange(ref curr, _header.Next);
while (curr != null && curr.value > newValue)
{
prev = Interlocked.Exchange(ref curr, curr.Next);
}
//need some locking around prev.next first, ensure not modified/deleted, etc..
//not in the scope of this question.
var newNode = new Node(newValue, prev.Next);
prev.Next = newNode;
}
I understand that, for example, curr = curr.next is an atomic read, but can I be sure that a specific thread will read the most up to date value of curr.next, without the Interlocked? 我知道,例如,curr = curr.next是原子读取,但是我可以确定特定线程在没有互锁的情况下将读取curr.next的最新值吗?
Using an Interlocked
method does two things: 使用Interlocked
方法有两件事:
Exchange
you doing the equivalent of: var temp = first; first=second; return temp;
对于Exchange
您可以执行以下操作: var temp = first; first=second; return temp;
var temp = first; first=second; return temp;
but without risk of either variable being modified by another thread while you're doing that. 但这样做时,没有任何风险被另一个线程修改的风险。 So, onto your code specifically. 因此,专门针对您的代码。 Your second solution isn't actually thread safe. 您的第二个解决方案实际上不是线程安全的。 Each individual Interlocked
operation is atomic, but any number of calls to various Interlocked
calls aren't atomic. 每个单独的Interlocked
操作都是原子的,但是对各种Interlocked
调用的任意数量的调用都不是原子的。 Given everything that you're methods are doing, your critical sections are actually much larger; 考虑到方法所执行的所有操作,实际上关键部分要大得多。 you'll need to use lock
or another similar mechanism (ie a semaphore or monitor) to limit access to sections of code to only a single thread. 您将需要使用lock
或其他类似的机制(即信号量或监视器)来将对代码段的访问限制为仅一个线程。 In your particular case I'd imagine that the entirety of the method is a critical section. 在您的特殊情况下,我想整个方法是关键部分。 If you're really, really careful you may be able to have several smaller critical blocks, but it will be very difficult to ensure that there are no possible race conditions as a result. 如果您真的很谨慎,也许可以有几个较小的关键区,但是要确保没有可能出现的竞赛条件将非常困难。
As for performance, well, as it is the code doesn't work, and so performance is irrelevant. 至于性能,因为代码不起作用,因此性能无关紧要。
Implementing a lock-free linked list is quite a bit more complicated than this. 实现无锁的链表比这要复杂得多。 I strongly recommend that you don't do it. 我强烈建议您不要这样做。 Just use plain old monitors instead. 只需使用普通的旧显示器即可。 If this is just for interests sake, than you need to do some research into lock-free algorithms. 如果只是出于兴趣,那么您需要对无锁算法进行一些研究。
To more directly answer your question, the cost of an interlocked operation depends on the amount of contention over the variables. 为了更直接地回答您的问题,互锁操作的成本取决于变量的争用量。 In a contention free scenario (eg single threaded), the cost is extremely minimal. 在无竞争的情况下(例如单线程),成本极低。 As the number of concurrent accesses rise, the cost increases. 随着并发访问次数的增加,成本也随之增加。 The reason is that an interlocked operation isn't really lock free. 原因是互锁操作并非真正无锁。 It's simply using a hardware lock instead of a software one. 它只是使用硬件锁而不是软件锁。 For this reason, lock-free algorithms often do not perform as well as you would intuitively expect. 因此,无锁算法的性能通常不如您所期望的那样好。
You are only protecting the assignments, but they are atomic anyway. 您只是在保护分配,但是无论如何它们都是原子的。 You should check if the Exchanged actually succeeded. 您应该检查Exchanged是否真的成功。
But that still wouldn't make your list thread-safe, a concurrent thread could be inserting a new value larger than your newValue before your insert point. 但这仍然不能使列表成为线程安全的,并发线程可能会在插入点之前插入一个比newValue大的新值。 I think the list could even end up being broken. 我认为该列表甚至可能最终被打破。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.