You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# AsParallel并行时Random生成重复随机字符串引发文件占用问题

问题场景

现有一个字符串数组,需要对数组内每个字符串执行对应处理任务,为提升处理效率采用并行方式执行,初始实现代码如下:

void Test()
{
    sourcefiles.AsParallel().ForAll(MyMethod);
}

private void MyMethod(string source)
{
   string tmp_string = RandomString(10);
   string path = Path.Combine(@"C:\myfolder", tmp_string);
   
  // 针对path的文件操作逻辑
}

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。

该现象不符合预期:理论上不同并行任务生成的随机文件名应当互不重复,且该问题稳定复现,并非偶发事件。即使提升RandomString()方法生成字符串的复杂度也无法解决,调试时观测到两个不同执行任务会生成完全相同的tmp_string值,其余任务生成值均不重复,具体运行日志如下:

PROCESS - RANDOM_STRING
1937 - "r6MbODsNcF1654683907030"
1374 - "MqrdQe386M1654683928872"  <---
1518 - "iX33edEA5F1654683928873"
1691 - "MqrdQe386M1654683928872"  <---
1486 - "u46vqUrt601654684013613"

日志说明:字符串前10位为RandomString()方法生成的随机字符,后续数字为Unix时间戳。
补充说明:有观点提出短时间内连续创建Random类实例会生成相同的种子,但该结论至少在.NET (Core)中并不成立。实际测试验证,通过加锁机制处理Random调用、或是采用线程安全的Guid生成随机字符串,均可正常解决该问题。

根因分析

问题核心原因是**Random类本身不是线程安全的**。

本场景中全局共用了一个静态Random实例,多线程并行调用Next()方法时,会出现内部状态损坏:如果两个线程同时触发Next()调用,会导致内部种子状态的计算出现同步问题,最终返回完全相同的随机值,这就是稳定出现重复文件名、进而引发文件占用异常的根本原因,和Random实例创建时的种子重复问题无关。

解决方案

可根据项目使用的.NET版本选择以下任意一种方案修复:

  • 方案1:对Random实例的调用加锁,保证同一时间只有一个线程执行Next()方法,避免并发修改实例内部状态,实现示例:
private static readonly Random random = new Random();
private static readonly object randLock = new object();

public static string RandomString(int length)
{
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    lock (randLock)
    {
        return new string(Enumerable.Repeat(chars, length)
            .Select(s => s[random.Next(s.Length)]).ToArray());
    }
}
  • 方案2:使用天然线程安全的Guid生成随机文件名,无需额外处理锁逻辑,实现示例:
public static string RandomString(int length)
{
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    // 若对随机字符范围有要求,也可基于Guid的字节值映射到指定字符集
    return Guid.NewGuid().ToString("N").Substring(0, length).ToUpper();
}
  • 方案3:如果项目基于.NET 6及以上版本,可以直接使用框架内置的线程安全全局随机实例Random.Shared,无需自行实现锁逻辑,直接替换原有全局random实例即可:
public static string RandomString(int length)
{
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    return new string(Enumerable.Repeat(chars, length)
    .Select(s => s[Random.Shared.Next(s.Length)]).ToArray());
}

内容的提问来源于stack exchange,提问作者Alessandro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 01:39:24