0

часть кода в методе запускаемом в 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 = &quot;&quot;;
        lock (locker)
        {
            string[] profile = File.ReadAllLines(&quot;listprofile.txt&quot;);
            List&lt;string&gt; newprofile = new List&lt;string&gt;();
            int count = profile.Where(x =&gt; x.Contains(domen)).Count();
            int rndtake = new Random().Next(1, count + 1);
            int num = 0;
            for (int i = 0; i &lt; profile.Length; i++)
            {
                if (profile[i].Contains(domen))
                {
                    num += 1;
                }
                if (profile[i].Contains(domen) &amp;&amp; folder == &quot;&quot;)
                {
                    folder = profile[i].Split(':')[1];
                }
                else
                {
                    newprofile.Add(profile[i]);
                }
            }
            if (folder != &quot;&quot;)
            {
                File.WriteAllLines(&quot;listprofile.txt&quot;, newprofile.ToArray());
                Thread.Sleep(3000);
            }
        }


        return folder;
    }

  • lock работает независимо от наличия семафора в коде. покажите код. –  Aug 16 '21 at 08:44
  • Локи работают везде, вопрос только в правильном использовании. Использовать потоки (Thread) на низком уровне - это неудобно и не эффективно. Чтобы вам помочь, надо знать задачу, которую вы решаете и видеть код, который не работает как надо. Так что вам нужно сделать? Зачем потоки? Зачем семафор? Кстати, вот есть такой пример. – aepot Aug 16 '21 at 08:47
  • добавил пример кода. при пошаговом выполнении, вижу что лок вроде работает, но когда запускаю в работу многопоток, то в дебаге вижу ошибку, которая возникает, если разные потоки запускается с одним и тем же значением, которое берётся в локе из файла с удалением, как будто лок не срабатывает – Архипов Владимир Aug 16 '21 at 08:55
  • а как Locker объявлен? просто статическая переменная? тогда должно правильно работать. –  Aug 16 '21 at 08:56
  • да, как статик переменная. и без семафора всё работало безупречно, может в каком-то другом коде есть логика ошибочная, но решил перепроверить и спросить про семафор – Архипов Владимир Aug 16 '21 at 08:58
  • покажите весь код, включая тот кусок, который работает неправильно –  Aug 16 '21 at 09:06

1 Answers1

2

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 будет работать без выноса в поле класса, там это починили на уровне фреймворка.

  • да, это исправило ситуацию. без семафора запускал потоки с промежутками времени и это не было заметно, а с семафором стало проблемой, на него и подумал. Спасибо! – Архипов Владимир Aug 16 '21 at 09:46
  • 2
    @АрхиповВладимир не понимаю, зачем вообще использовать древний фреймворк для новой разработки. И зачем вам вообще new Thread, когда есть Task.Run. Если вас смущает асинхронный метод, а нужно просто запустить и забыть, то есть ThreadPool.QueueUserWorkItem - его и используйте для запуска потоков вместо Thread, если Task.Run не устраивает. А если уж взялись за Thread - важно понимать, чем Background поток отличается от обычного, внутри этого понимания закопан сюрприз в виде зависания всего приложения в системных задачах при выходе. – aepot Aug 16 '21 at 10:32
  • когда изучал многопоточность Thread был основным в понимании, так и пошло, таски даже не рассматривал, сейчас уже есть понимание тасков, но по привычке thread использую – Архипов Владимир Aug 16 '21 at 11:01
  • @aepot - так же у таска нет abort() и если зависает из-за чужой библиотеки непонятно пока - что делать в таких случаях – Архипов Владимир Aug 16 '21 at 13:49
  • 2
    @АрхиповВладимир Thread.Abort() является устаревшим и уже выпилен из дотнета. Вопрос надежного прибивания чужого кода очень сложный, и аборт не спасет. Попробуйте запустить поток с кодом try { Thread.Sleep(int.MaxValue); } finally { while (true) { } } этот код повесит поток намертво и вы не сможете его убить, никак, аборт не поможет. Единственный способ гарантированно убить поток - это запустить его в отдельном процессе и убить процесс средствами операционки. Не используйте Abort(), он не дает никаких гарантий. – aepot Aug 16 '21 at 13:59
  • @aepot, спасибо за совет с отдельным процессом, в текущей работе, это наверное будет лучшим решением – Архипов Владимир Aug 17 '21 at 08:36