最近我正在阅读.NET Hashtable的实现,遇到了一些我不理解的代码.部分代码是:
int num3 = 0;
int num4;
do
{
num4 = this.version;
bucket = bucketArray[index];
if (++num3 % 8 == 0)
Thread.Sleep(1);
}
while (this.isWriterInProgress || num4 != this.version);
Run Code Online (Sandbox Code Playgroud)
整个代码是内public virtual object this[object key]的System.Collections.Hashtable(mscorlib程序版本= 4.0.0.0).
问题是:
在Thread.Sleep(1)那里的原因是什么?
我查看了并发集合,但它们似乎在引擎盖下使用了正常锁定..Net框架中是否有任何使用此锁定构造的好例子?
我最初看过ConcurrentDictionary.我看到它使用普通锁,但BlockingCollection类在其内部方法中使用SpinWait.
以下是基于的互锁方法的实现Interlocked.CompareExchange。
这段代码SpinWait在重复之前是否宜使用自旋?
public static bool AddIfLessThan(ref int location, int value, int comparison)
{
int currentValue;
do
{
currentValue = location; // Read the current value
if (currentValue >= comparison) return false; // If "less than comparison" is NOT satisfied, return false
}
// Set to currentValue+value, iff still on currentValue; reiterate if not assigned
while (Interlocked.CompareExchange(ref location, currentValue + value, currentValue) != currentValue);
return true; // Assigned, so return true
}
Run Code Online (Sandbox Code Playgroud)
我已经看到SpinWait在这种情况下使用过,但是我的理论是它应该是不必要的。毕竟,循环仅包含少量指令,并且总是有一个线程在进行中。
假设有两个线程竞相执行此方法,并且第一个线程立即成功执行,而第二个线程最初不做任何更改,必须重申。没有其他竞争者,第二个线程是否有可能在第二次尝试时失败 …
MSDN "线程安全集合.NET Framework 4"声明:
"一些并发集合类型使用轻量级同步机制,如SpinLock,SpinWait, SemaphoreSlim和CountdownEvent,它们是.NET Framework 4中的新增功能"
虽然MSDN网站告诉SpinWaitwas可用于.NET 1.1,而另一篇MSDN文章从.NET 4.0启动SpinWaitwas
好吧,好奇心来自Lee Grissom的评论,以回答SynchronizedCollection与其他并发集合之间的区别是什么?:
"@Matt,.NET4并发类使用
SpinWait对象来解决线程安全而不是Monitor.Enter/Exit(又称Critical部分)?"
以及第一个NSDN引用说明这SpinWait是.NET 4.0的新功能
那么,它是新的还是不是?
如果新的那么如何?
我开发了一个类似于 Java for C++ 的监视器对象,并进行了一些改进。主要的改进是不仅有一个用于锁定和解锁的自旋循环,而且还有一个用于等待事件的自旋循环。在这种情况下,您不必锁定互斥体,而是在 wait_poll 函数上提供谓词,并且代码反复尝试锁定互斥体轮询,如果它可以锁定互斥体,则它会调用返回(或移动)一对的谓词bool 和结果类型。
即使调用立即返回,等待内核中的信号量和/或事件对象 (Win32) 也很容易花费 1.000 到 10.000 个时钟周期,因为之前已经设置了信号量或事件。因此,必须有一个与该等待间隔具有合理关系的旋转计数,其中旋转的时间是内核中所花费的最小间隔的十分之一。
通过我的监控对象,我从 glibc 中获取了自旋计数重新计算算法。我还使用暂停指令。但我发现在我的CPU(TR 3900X)上暂停指令太快了。平均约为 0.78ns。在 Intel-CPU 上,它更合理,约为 30 纳秒。
这是代码:
#include <iostream>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <immintrin.h>
using namespace std;
using namespace chrono;
int main( int argc, char **argv )
{
static uint64_t const PAUSE_ROUNDS = 1'000'000'000;
auto start = high_resolution_clock::now();
for( uint64_t i = PAUSE_ROUNDS; i; --i )
_mm_pause();
double ns = (int64_t)duration_cast<nanoseconds>( high_resolution_clock::now() - start ).count() / (double)PAUSE_ROUNDS;
cout << …Run Code Online (Sandbox Code Playgroud) spinwait ×5
.net ×3
c# ×3
spinlock ×2
assembly ×1
concurrency ×1
hashtable ×1
interlocked ×1
thread-sleep ×1
x86 ×1