2016-07-22 7 views
2

Ich habe eine Schleife in meinem Codewie zum Wechselstrom # threadsicher Zufallszahlengenerator

Parallel.For(0, Cnts.MosqPopulation, i => { DoWork() }); 

jedoch in der DoWork() Funktion gibt es mehrere Anrufe zu einem Zufallszahlengenerator herzustellen, die wie folgt definiert ist:

public static class Utils 
{ 
    public static readonly Random random = new Random(); 

} 

Es ist statische Instanz, so dass es nur einmal gesetzt wird. Und ich kann es im gesamten Code verwenden.

Laut MSDN und anderen Stackoverflow-Threads ist dies nicht threadsicher. Tatsächlich habe ich manchmal bemerkt, dass mein Code bricht und der Zufallszahlengenerator beginnt, alle Nullen zu erzeugen (gemäß der MSDN-Dokumentation).

Es gibt andere stackoverflow-Threads, aber sind ziemlich alt und die Implementierung ist langsam. Ich kann es mir nicht leisten, beim Generieren der Zahlen Zeit zu verlieren, da das Programm eine wissenschaftliche Berechnung ist und Hunderte von Simulationen durchführt.

Ich habe seit 2.0 Tagen nicht mehr mit .net gearbeitet und bin mir nicht sicher, wie sich die Sprache entwickelt hat, um einen schnellen, effizienten und threadsicheren RNG zu erstellen.

Hier sind die bisherigen Themen:

Is C# Random Number Generator thread safe?

Correct way to use Random in multithread application

Fast thread-safe random number generator for C#

Hinweis: weil ich eine schnelle Implementierung benötigen, kann ich nicht die RNGCryptoServiceProvider verwenden, die eher langsam ist.

Anmerkung2: Ich habe keinen minimalen Arbeitscode. Ich weiß nicht einmal, wo ich anfangen soll, da ich nicht weiß, wie Thread-Sicherheit funktioniert oder hohes Wissen über C# hat. Es scheint also, als ob ich nach einer vollständigen Lösung frage.

+0

Warum sind die Lösungen in den Beiträge zu erstellen, die Sie nicht für Sie geeignet verlinken ? Irgend ein bestimmter Grund? –

+0

Einige von ihnen sind wirklich nicht Thread-Safe wie in ihren Kommentaren. Die anderen sind zu langsam. Ich hatte gehofft, dass es eine * moderne * Version der vorherigen Antworten gibt. – masfenix

+1

Also wie wäre es mit diesem: http://Stackoverflow.com/a/19271062/40347 –

Antwort

2

Mit dem Attribut ThreadStatic und einem benutzerdefinierten Getter erhalten Sie eine einzige Random Instanz pro Thread. Wenn dies nicht akzeptabel ist, verwenden Sie Sperren.

public static class Utils 
{ 
    [ThreadStatic] 
    private static readonly Random __random; 

    public static Random Random => __random??(__random=new Random()); 
} 

Die ThreadStatic Attribut nicht läuft die Initialisierung auf jedem Thread, so dass Sie zu tun, um in Ihrer Accessor verantwortlich sind. Denken Sie auch an Ihre Samen initializer, können Sie so etwas wie

new Random((int) ((1+Thread.CurrentThread.ManagedThreadId) * DateTime.UtcNow.Ticks)) 
0

verwenden Wenn Sie Wissen, wie viele Threads Sie parallel laufen diese arbeiten kann:

Random rand = new Random(); 
var randomNums = Enumerable.Range(0, Cnts.MosqPopulation) 
          .Select(_ => rand.Next()).ToList(); 
Parallel.For(0, Cnts.MosqPopulation, i => 
{ 
    Random localRand = new Random(randomNums[i]); 
    DoWork(); 
}); 

Nicht sicher, wie nicht zu unterscheiden die resultierende Verteilung wäre aber von einem einheitlichen.

0

würde ich so etwas wie dies berücksichtigen:

private static int _tracker = 0; 

private static ThreadLocal<Random> _random = new ThreadLocal<Random>(() => { 
    var seed = (int)(Environment.TickCount & 0xFFFFFF00 | (byte)(Interlocked.Increment(ref _tracker) % 255)); 
    var random = new Random(seed); 
    return random; 
}); 

ich in diesen Tagen kein großer Fan von ThreadStatic bin. Wir haben bessere Werkzeuge als das mit ThreadLocal. Verwenden Sie einfach _random.Value in Ihrer parallelen Schleife und es wird Ihnen eine neue Random pro Thread geben.

Es kombiniert einen atomar steigenden Wert sowie das Standardverhalten von Environemnt.TickCount. Der inkrementierende Wert ist da, um das Problem zu lösen, dass zwei Random denselben Samen bekommen. Beachten Sie, dass mit diesem Ansatz nur 255 Randoms erstellt werden können. Wenn Sie mehr benötigen, ändern Sie die Größe der Maske.

Wie Sie bereits bemerkt haben, ist dies nicht für sichere Zwecke verwendbar.

-1

Sie von Random erben kann ein Thread sichere Zufalls Klasse

public class ThreadsafeRandom : Random 
{ 
    private readonly object _lock = new object(); 

    public ThreadsafeRandom() : base() { } 
    public ThreadsafeRandom(int Seed) : base(Seed) { } 

    public override int Next() 
    { 
     lock (_lock) 
     { 
      return base.Next(); 
     } 
    } 

    public override int Next(int maxValue) 
    { 
     lock (_lock) 
     { 
      return base.Next(maxValue); 
     } 
    } 

    public override int Next(int minValue, int maxValue) 
    { 
     lock (_lock) 
     { 
      return base.Next(minValue, maxValue); 
     } 
    } 

    public override void NextBytes(byte[ ] buffer) 
    { 
     lock (_lock) 
     { 
      base.NextBytes(buffer); 
     } 
    } 

    public override double NextDouble() 
    { 
     lock (_lock) 
     { 
      return base.NextDouble(); 
     } 
    } 
} 

und eine Instanz dieser Klasse

public static class Utils 
{ 
    public static readonly Random random = new ThreadsafeRandom(); 

} 
+0

Die Verriegelung ist das letzte Instrument, das verwendet werden muss, besonders in diesen Fällen – ZOXEXIVO