Работает ли lock внутри потоков, запущенных в semaphoreslim?

часть кода в методе запускаемом в Thread ограждена lock(locker){для 1 потока} без семафора потоки работают корректно. в семафоре такое ощущение, что локер не работает Если в семафоре не работает локер, то как ограничить часть кода только для 1го, если семафор запускать (5,5) например?

код запуска потоков

_pool = new SemaphoreSlim(5, 5);

await Task.Run(() => 
{
   for (int i = 0; i < 2000; i++)
       {
          Thread t = new Thread(new ParameterizedThreadStart(CircleForPool));

          t.Start(null);
       }
});

код метода:

public void CircleForPool(object obj)
      {
          _pool.Wait();
          //некоторый код
          lock(Locker)
              {
                 //некоторый код
              }
          //некоторый код
          _pool.Release();
       }




так же замечено странное поведение в семафоре, кода, который не лочится дополнительно:

string[] lines = File.ReadAllLines("keys.txt");
KeyApi = lines[new Random().Next(lines.Length)];

тут берётся случайное значение из файла, в котором около 200 строк, и на старте сразу 6 потоков из 10 (ограниченных семафором) берут один и тот же ключ

так же подобный код в локе (этот метод вызывается внутри потока):

public string TakeFolder(string domen)
        {
            
            string folder = "";
            lock (locker)
            {
                string[] profile = File.ReadAllLines("listprofile.txt");
                List<string> newprofile = new List<string>();
                int count = profile.Where(x => x.Contains(domen)).Count();
                int rndtake = new Random().Next(1, count + 1);
                int num = 0;
                for (int i = 0; i < profile.Length; i++)
                {
                    if (profile[i].Contains(domen))
                    {
                        num += 1;
                    }
                    if (profile[i].Contains(domen) && folder == "")
                    {
                        folder = profile[i].Split(':')[1];
                    }
                    else
                    {
                        newprofile.Add(profile[i]);
                    }
                }
                if (folder != "")
                {
                    File.WriteAllLines("listprofile.txt", newprofile.ToArray());
                    Thread.Sleep(3000);
                }
            }


            return folder;
        }

Ответы (1 шт):

Автор решения: PashaPash

new Random() использует текущее время в качестве начального seed. Поэтому когда вы вызываете new Random() одновременно вне лока - вы получаете один и тот же ключ. Используйте общий объект Random - положите его в поле или в статику, и оберните вызов Next в лок (с отдельным locker).

https://docs.microsoft.com/en-us/dotnet/api/system.random.-ctor?view=net-5.0#System_Random__ctor

In .NET Framework, the default seed value is derived from the system clock, which has finite resolution. As a result, different Random objects that are created in close succession by a call to the parameterless constructor have identical default seed values and, therefore, produce identical sets of random numbers. You can avoid this problem by using a single Random object to generate all random numbers.

В .NET Core будет работать без выноса в поле класса, там это починили на уровне фреймворка.

→ Ссылка