Запуск нескольких потоков, почему вы должны ждать?

#c# #multithreading #task

#c# #многопоточность #задача

Вопрос:

Я поиграл с потоками и задачами (.net 4) и заметил некоторое странное поведение, когда вы запускаете несколько потоков, не дожидаясь нескольких миллисекунд между каждым вызовом, запущенным потоком.

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

 1
2
1
2
 

Но вместо этого только выводит:

 2
2
2
2
 

Ниже приведен код, который я запускаю.

 public static void Main()
{
    var items = new[] {"1", "2"};
    foreach (var item in items)
    {
      var thread = new Thread(() => Print(item));          
      thread.Start();
      //var task = Task.Factory.StartNew(() => Print(item));               
    }
}

static void Print(string something)
{
  while (true)
  {
    Console.WriteLine(something);
    Thread.Sleep(1000);
  }
}
 

Теперь, когда я вызываю Thread .Спящий режим (50) после потока.Start() только тогда результат выглядит так, как ожидалось

 1
2
1
2
 

Мой вопрос:

  • Почему, когда вы не ждете между запуском обоих потоков, первый поток теряет значение параметра метода, с которым вы изначально его запустили?

т.е. Первый поток запускается с параметром «1», второй поток запускается с параметром «2», однако параметр первого потока также становится «2»? Это не имеет смысла, тем более, что параметр метода Print() является типом значения string .

Комментарии:

1. Спасибо за все ответы.

Ответ №1:

Google «доступ к измененному закрытию». Происходит то, что ваша локальная переменная «item» изменяет свое значение до вызова функции печати. Решением было бы создать новую переменную внутри области действия цикла и назначить ей элемент.

Ответ №2:

Элемент вычисляется во время запуска созданного вами потока из-за закрытия c #. Другой способ принудительно вычислить элемент — ввести переменную, чтобы закрытие включало ее следующим образом:

 foreach (var item in items)     
        {
            var closedItem = item;
            var thread = new Thread(() => Print(closedItem));                 
            thread.Start();       
        } 
 

Ответ №3:

Ваша проблема не в потоках. Ваша проблема связана с закрытием и foreach. Вы можете прочитать здесь, почему: http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

Когда вы играете с синхронизацией потоков, вы также переупорядочиваете тайминги основного потока, поэтому иногда цикл будет выполняться до запуска метода печати нового потока, а иногда и после.

Ответ №4:

Покажите нам код запуска потока, и вы обнаружите, что вы передаете не постоянную строку, а ссылочную переменную, и в промежутках между вызовами этих методов запуска вы, вероятно, меняете переменную.