最近我正在阅读.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);
整个代码在public virtual object this[object key]
的{{1}}范围内(mscorlib Version = 4.0.0.0)。
问题是:
在那里System.Collections.Hashtable
的原因是什么?
答案 0 :(得分:70)
Sleep(1)是Windows中一种记录的方式,用于生成处理器并允许其他线程运行。您可以在参考源中找到带有注释的代码:
// Our memory model guarantee if we pick up the change in bucket from another processor,
// we will see the 'isWriterProgress' flag to be true or 'version' is changed in the reader.
//
int spinCount = 0;
do {
// this is violate read, following memory accesses can not be moved ahead of it.
currentversion = version;
b = lbuckets[bucketNumber];
// The contention between reader and writer shouldn't happen frequently.
// But just in case this will burn CPU, yield the control of CPU if we spinned a few times.
// 8 is just a random number I pick.
if( (++spinCount) % 8 == 0 ) {
Thread.Sleep(1); // 1 means we are yeilding control to all threads, including low-priority ones.
}
} while ( isWriterInProgress || (currentversion != version) );
isWriterInProgress变量是一个易变的bool。作者遇到一些麻烦,英语“违反读”是“易读”。基本思路是尽量避免屈服,线程上下文切换非常昂贵,希望编写者能够快速完成。如果没有平移,那么明确屈服以避免烧毁cpu。这可能是今天用Spinlock编写的,但Hashtable很老。关于记忆模型的假设也是如此。
答案 1 :(得分:7)
如果无法访问其余的实施代码,我只能根据您发布的内容进行有根据的猜测。
也就是说,看起来它正在尝试更新Hashtable中的内容,无论是在内存中还是在磁盘上,并在等待它完成时进行无限循环(通过检查isWriterInProgress
)。< / p>
如果它是单核处理器,它一次只能运行一个线程。像这样连续循环可能很容易意味着其他线程没有机会运行,但Thread.Sleep(1)
使处理器有机会给作者留出时间。没有等待,作者线程可能永远不会有机会运行,也永远不会完成。
答案 2 :(得分:5)
我没有读过源代码,但它看起来像一个无锁的并发性东西。您正在尝试从哈希表中读取,但其他人可能正在写入它,因此您要等到isWriterInProgress
未设置且您已阅读的版本未更改。
这并不能解释为什么例如我们总是至少等一次。编辑:那是因为我们没有,感谢@Maciej指出这一点。如果没有争用,我们立即进行。我不知道为什么8是神奇数字而不是例如但是,4或16。