2012-03-16 23 views
8

Đây là đỉnh cao của một Skeet posting for a random provider:Thực hiện một nhà cung cấp ngẫu nhiên trong Net 3.5

public static class RandomProvider 
{  
    private static int seed = Environment.TickCount; 

    private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random>(() => 
     new Random(Interlocked.Increment(ref seed)) 
    ); 

    public static Random GetThreadRandom() 
    { 
     return randomWrapper.Value; 
    } 
} 

Tôi muốn sử dụng các khái niệm tương tự trong một dự án .NET 3.5, vì vậy ThreadLocal không phải là một lựa chọn.

Bạn sẽ sửa đổi mã để có một nhà cung cấp ngẫu nhiên an toàn theo chủ đề mà không cần sự trợ giúp của ThreadLocal?

CẬP NHẬT

Ok, đồng hành cùng Simon's [ThreadStatic] vì tôi hiểu điều đó là tốt nhất. Rất nhiều thông tin tốt ở đây để xem xét và suy nghĩ lại khi thời gian cho phép. Cảm ơn tất cả!

public static class RandomProvider 
{ 
    private static int _seed = Environment.TickCount; 

    [ThreadStatic] 
    private static Random _random; 

    /// <summary> 
    /// Gets the thread safe random. 
    /// </summary> 
    /// <returns></returns> 
    public static Random GetThreadRandom() { return _random ?? (_random = new Random(Interlocked.Increment(ref _seed))); } 
} 
+1

Ngoài ra còn có một tiếp theo lên bài viết: https://msmvps.com/blogs/jon_skeet/archive/2009/11/04/revisiting-randomness.aspx nơi ông thảo luận về các chiến lược an toàn chủ đề khác. – CodesInChaos

Trả lời

8

Bạn có thể sử dụng ThreadStaticAttribute

[ThreadStatic] 
private static Random _random; 

private static Random Random 
{ 
    get 
    { 
     int seed = Environment.TickCount; 

     return _random ?? (_random = new Random(Interlocked.Increment(ref seed))) 
    } 
} 
+0

Đẹp và đáng giá +1, nhưng âm thanh quá dễ, sau khi đọc xong Skeet. Việc triển khai ThreadLocal sẽ có lợi thế gì? – Berryl

+0

Tôi không biết về 'ThreadLocal' trước câu hỏi này, nhưng ngay từ cái nhìn đầu tiên, lợi thế duy nhất tôi thấy là' ThreadLocal' có thể đưa ra phương thức factory để khởi tạo giá trị, vì vậy bạn không phải kiểm tra ' null' – Terkel

+0

Skeets post dường như chỉ ra rằng 'ThreadLocal' là nhanh hơn một chút, nhưng trong các bài kiểm tra của riêng tôi, tôi không thể tái tạo nó. Tôi đã đi với 'ThreadLocal' cho thư viện máy phát ngẫu nhiên của tôi, nhưng tiếc là nó sử dụng các hợp đồng mã, và do đó chỉ hoạt động trên .net 4. – CodesInChaos

9

Làm thế nào bạn sẽ thay đổi mã để có một sợi cung cấp ngẫu nhiên an toàn mà không cần sự giúp đỡ của ThreadLocal?

Jon trả lời câu hỏi của bạn trong bài viết bạn liên quan đến:

sử dụng một ví dụ, mà còn sử dụng một khóa mà mọi người gọi ta phải nhớ để có được trong khi họ đang sử dụng bộ tạo số ngẫu nhiên. Điều đó có thể được đơn giản hóa bằng cách sử dụng một wrapper mà không khóa cho bạn, nhưng trong một hệ thống multithreaded rất nhiều bạn vẫn sẽ có khả năng lãng phí rất nhiều thời gian chờ đợi cho ổ khóa.

Vì vậy, hãy khóa nó mỗi lần trong trình bao bọc và mở khóa khi bạn hoàn tất.

Nếu đó là đủ rẻ, tuyệt vời, nó đủ rẻ.

Nếu điều đó không đủ rẻ thì bạn có hai lựa chọn. Trước tiên, làm cho nó rẻ hơn. Thứ hai, viết trình tạo luồng an toàn của trình tạo số giả ngẫu nhiên có thể được sử dụng mà không cần khóa.

Có một số cách để làm cho nó rẻ hơn. Ví dụ, bạn có thể giao dịch không gian theo thời gian; bạn có thể tạo ra một mảng của một trăm nghìn số ngẫu nhiên khi chương trình khởi động, và sau đó viết một thuật toán không khóa, làm cho các giá trị ngẫu nhiên được tính trước đó từ mảng. Khi bạn hết các giá trị, tạo ra một trăm nghìn giá trị trong một mảng, và trao đổi mảng mới cho mảng cũ.

Điều đó có nhược điểm là mức tiêu thụ bộ nhớ của nó lớn gấp hàng trăm nghìn lần so với mức có thể và hàng trăm nghìn số đột nhiên bị chậm và sau đó tăng tốc trở lại. Nếu điều đó là không thể chấp nhận thì sẽ đưa ra một chiến lược có thể chấp nhận được. Bạn là người biết hiệu suất chấp nhận được là gì và cái gì không.

Hoặc, như tôi đã nói, viết của riêng bạn nếu bạn không thích cái được cung cấp cho bạn. Viết một thực hiện threads an toàn với hiệu suất chấp nhận được và sử dụng nó từ nhiều luồng.

Tôi đã thấy những gì Jon nói về khóa trong trình bao bọc nhưng không chắc chắn mã sẽ trông như thế nào!

Cái gì như:

sealed class SafeRandom 
{ 
    private Random random = new Random(); 
    public int Next() 
    { 
     lock(random) 
     { 
      return random.Next(); 
     } 
    } 
} 

Bây giờ mỗi khi bạn gọi Tiếp theo, bạn đưa ra một khóa. (Luôn khóa trên một đối tượng riêng riêng; theo cách đó bạn biết rằng mã của bạn là mã duy nhất khóa nó!) Nếu hai chủ đề gọi Tiếp theo "cùng lúc" thì khối "thua" cho đến khi lá "thắng" Phương thức tiếp theo.

Nếu bạn muốn, bạn thậm chí có thể làm cho các đối tượng SafeRandom một lớp tĩnh:

static class SafeRandom 
{ 
    private static Random random = new Random(); 
    public static int Next() 
    { 
     lock(random) 
     { 
      return random.Next(); 
     } 
    } 
} 

và bây giờ bạn có thể gọi SafeRandom.Next() từ bất kỳ thread.

+0

Hmmm, giống như * bạn * biết bạn đang nói về cái gì: -) – Berryl

+0

Có, tôi đã thấy những gì Jon nói về khóa trong trình bao bọc nhưng không chắc chắn mã sẽ trông như thế nào! – Berryl

+0

@Berryl: Xem cập nhật của tôi. –

1

Tôi chỉ đọc liên kết đến "C# trong chiều sâu" và, mặc dù tôi đồng ý với việc Random không phải là luồng an toàn là một nỗi đau, tôi thực sự sẽ sử dụng một cách tiếp cận khác để loại bỏ vấn đề, cụ thể là vì lý do hiệu suất .

Instantiating một cơ ngẫu nhiên là một hành động khá nặng nề, vì vậy tôi thà giữ chỉ một trường hợp và làm cho nó chủ đề an toàn thông qua việc sử dụng các ổ khóa:

public static class RandomProvider 
{  
    private static Random randomEngine = new Random(Environment.TickCount); 

    private static object randomLock = new object(); 

    public static int GetRandomValue() 
    { 
     lock(randomLock) 
     { 
      return randomEngine.Next(); 
     } 
    } 
} 

HTH

+0

Ý của bạn là đăng mã khác? Điều này có vẻ giống như mã 4.0 gốc, phải không? – Berryl

+0

Không: nó không còn sử dụng ThreadLocal nữa, và cũng không sử dụng ThreadStatic –

Các vấn đề liên quan