我調試了Paint.Net plugin的一些問題,當幾個線程從一個實例調用一個方法時,我偶然發現了Random類的一些問題。隨機在.NET中的併發問題?
由於一些奇怪的原因,似乎如果我不阻止併發訪問,通過同步被調用的方法,我的隨機實例開始行爲......隨機(但意義不大)。
在下面的例子中,我創建了數百個線程,它們重複調用一個Random對象。當我運行它時,我有時(並不總是,但幾乎)得到明顯錯誤的結果。如果我取消註釋Synchronized
方法註釋,則不會發生此問題。
using System;
using System.Threading;
using System.Runtime.CompilerServices;
namespace testRandom {
class RandTest {
static int NTIMES = 300;
private long ac=0;
public void run() { // ask for random number 'ntimes' and accumulate
for(int i=0;i<NTIMES;i++) {
ac+=Program.getRandInt();
System.Threading.Thread.Sleep(2);
}
}
public double getAv() {
return ac/(double)NTIMES; // average
}
}
class Program
{
static Random random = new Random();
static int MAXVAL = 256;
static int NTREADS = 200;
//[MethodImpl(MethodImplOptions.Synchronized)]
public static int getRandInt() {
return random.Next(MAXVAL+1); // returns a value between 0 and MAXVAL (inclusive)
}
public static void Main(string[] args) {
RandTest[] tests = new RandTest[NTREADS];
Thread[] threads = new Thread[NTREADS];
for(int i=0;i<NTREADS;i++) {
tests[i]= new RandTest();
threads[i] = new Thread(new ThreadStart(tests[i].run));
}
for(int i=0;i<NTREADS;i++) threads[i].Start();
threads[0].Join();
bool alive=true;
while(alive) { // make sure threads are finished
alive = false;
for(int i=0;i<NTREADS;i++) { if(threads[i].IsAlive) alive=true; }
}
double av=0;
for(int i=0;i<NTREADS;i++) av += tests[i].getAv();
av /= NTREADS;
Console.WriteLine("Average:{0, 6:f2} Expected:{1, 6:f2}",av,MAXVAL/2.0);
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
一個例子輸出中(與上面的值):
Average: 78.98 Expected:128.00
Press any key to continue . . .
這是一些已知的問題?從多個線程中調用Random對象而不同步是不正確的?
更新:根據答案,文檔指出隨機方法不是線程安全的 - mea culpa,我應該讀取它。也許我之前已經閱讀過這篇文章,但並不認爲它如此重要 - 人們可能會認爲,在兩個線程同時進入相同方法的情況下,最糟糕的情況是這些調用會得到錯誤的結果 - 而不是一個巨大的交易,如果我們不太關心隨機數的質量......但這個問題真的是災難性的,因爲對象處於不一致的狀態,並且從這個狀態返回返回爲零 - 如註釋here。
除非文檔聲明相反你可以假定任何類都支持訪問單個實例的多個線程。 – CodesInChaos 2011-07-16 07:55:26