簡體   English   中英

C#多個進程生成相同的隨機字符串

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM