为什么.NET内部Hashtable中有Thread.Sleep(1)?

Dar*_*iak 70 .net c# hashtable thread-sleep spinwait

最近我正在阅读.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)那里的原因是什么?

Han*_*ant 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) );
Run Code Online (Sandbox Code Playgroud)

isWriterInProgress变量是一个易失性bool.作者遇到一些麻烦,英语"违反读"是"易读".基本思路是尽量避免屈服,线程上下文切换非常昂贵,希望编写者能够快速完成.如果没有平移,那么明确屈服以避免烧毁cpu.这可能是今天用Spinlock编写的,但Hashtable很老.关于内存模型的假设也是如此.

  • 如果另一个线程准备好以更高的优先级运行,则Sleep(0)仅产生.这不是意图. (11认同)
  • 我想如果你想要一个收益率,推荐"Sleep(0)"."睡眠(1)"实际上是一个睡眠. (8认同)
  • 等于或高于.写入花费很少的时间,隐含的是写入线程必须已被暂停,如果这在8次尝试后不起作用.这不是很棒的代码. (4认同)

Tar*_*rka 7

如果无法访问其余的实现代码,我只能根据您发布的内容进行有根据的猜测.

也就是说,看起来它正在尝试更新Hashtable中的内容,无论是在内存中还是在磁盘上,并在等待它完成时进行无限循环(通过检查看到isWriterInProgress).

如果它是单核处理器,它一次只能运行一个线程.像这样连续循环可能很容易意味着其他线程没有机会运行,但是Thread.Sleep(1)让处理器有机会给作者留出时间.如果没有等待,作者线程可能永远不会有机会运行,也永远不会完成.


Dav*_*ler 5

我没有读过源代码,但它看起来像一个无锁的并发性东西.您正在尝试从哈希表中读取,但是其他人可能正在写入它,因此您要等到isWriterInProgress未设置并且您已阅读的版本未更改.

这并不能解释为什么我们总是至少等待一次.编辑:那是因为我们没有,感谢@Maciej指出这一点.如果没有争用,我们立即进行.我不知道为什么8是神奇数字而不是例如4或16.

  • 不,我们不.在`++ num3`之后,`num3`将是1.或者我严重缺乏咖啡因. (3认同)
  • 如果我们在每次调用[]运算符时调用Sleep(1),那对于性能来说会非常糟糕.请记住,Thread.Sleep(1)实际上并没有让你睡1毫秒 - 它更像是15. (3认同)
  • @BlackBear - 它扔我在第一次了,但是当你意识到`version`可能在哈希表是指数据的版本(即每个写创建一个新的),它有一定道理:) (2认同)