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.
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