[英]C# Multiple processes generate the same random string
我有一個字符串數組。 對於每個字符串,我必須完成一項任務,並且我想並行執行。 這是我的一段代碼:
void Test()
{
sourcefiles.AsParallel().ForAll(MyMethod);
}
private void MyMethod(string source)
{
string tmp_string = RandomString(10);
string path = Path.Combine(@"C:\myfolder", tmp_string);
// (File operations on `path` go here)
}
private static Random random = new Random();
public static string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
我收到以下異常: Cannot access C:\myfolder\ABCDEFG because it is in use by another process
這很奇怪,因為字符串“ABCDEFG”應該是由不同進程生成的隨機字符串。 這不是一個不幸的事件,它一直在發生。 我還增加了RandomString()
的復雜性,但沒有任何反應,事實上我一直從調試中看到有兩個不同的進程具有相同的tmp_string
(其他進程有不同的進程)。 哪里錯了?
對於上下文,這就是發生的事情:
PROCESS - RANDOM_STRING
1937 - "r6MbODsNcF1654683907030"
1374 - "MqrdQe386M1654683928872" <---
1518 - "iX33edEA5F1654683928873"
1691 - "MqrdQe386M1654683928872" <---
1486 - "u46vqUrt601654684013613"
前 10 個字母數字字符來自RandomString()
函數,后面的數字代表 Unix 的紀元
編輯你們中的一些人建議快速連續創建類 Random(),將生成相同的種子。 正如一些評論所說,這不是真的,至少在 .NET (Core) 中是這樣。 通過使用 lock 或使用已接受答案中的代碼來解決它,它可以正常工作,因為(據我所知)Guid 是線程安全的。
您的RandomString
實現正在使用Random
的無參數構造函數,如果快速連續創建該構造函數,則會使用相同的種子,因此會產生相同的輸出。
正如您所說,您正在使用上面給定的代碼運行不同的進程。 在此代碼中,您使用Random
的單個靜態實例。 到目前為止,一切都很好。
不幸的是,在創建時使用Random
類作為當前時間的初始種子。 所以如果你在一個緊密的循環中創建多個進程,你很有可能兩個進程使用相同的時間作為隨機類的種子,這會導致產生相同的數字。
正如您已經在代碼中顯示的那樣,您似乎已經可以訪問進程 ID。 所以也許你應該從當前時間和進程ID為隨機類生成你自己的種子值,也許通過調用這樣的東西:
var seed = HashCode.Combine(DateTime.UtcNow, processId);
var random = new Random(seed);
盡管如此,即使您可以降低命中相同值的概率,您也應該檢查您的代碼是否已經存在這樣的目錄,如果存在,只需使用下一個隨機值開始新的嘗試。
最后一個更簡單的技巧:
如果您不關心文件夾的名稱,也可以使用Guid.NewGuid().ToString()
作為隨機文件夾名稱。 底層實現使用更多的種子源,然后只有時間和 128 位范圍應該將概率移動到足夠低以滿足您的需要。
以當前時間為種子,在短時間內多次創建Random
實例。 實際上,以這種方式生成的許多偽隨機數序列可能是相同的。
要使每個Random
實例創建不同的隨機值流,您需要為它們提供隨機種子 - 例如 GUID:
private static Random random = new Random(Guid.NewGuid().GetHashCode());
在這個特定的代碼示例中,同時使用了相同的Random
實例,這是不安全的。 您可以修改RandomString
方法以在每次使用時創建一個新的Random
實例:
public static string RandomString(int length)
{
Random rnd = new Random(Guid.NewGuid().GetHashCode());
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[rnd.Next(s.Length)]).ToArray());
}
隨機不是線程安全的! 因此,您的代碼不正確,因為它同時使用來自多個線程的單個隨機對象。 所以第一步應該是確保線程安全,要么使用鎖,要么為每個線程創建一個單獨的隨機對象。 如果您選擇后一種解決方案,則需要確保每個隨機對象的種子不同。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.