Состояние гонки для запуска потока - PullRequest
1 голос
/ 09 февраля 2011

Аналогичный вопрос был задан здесь , но ответы, как правило, все относятся к лямбда-нотации.Я получаю аналогичный результат без лямбды, поэтому я подумал, что попросил бы уточнить:

Скажем, у меня что-то вроде этого:

for (int i = 0; i < 5; i++)
  (new Thread(new ThreadStart(delegate()
    {
      Console.WriteLine("Thread " + i);
    }))).Start();

Можно ожидать следующий вывод:

Thread 0
Thread 1
Thread 2
Thread 3
Thread 4

Теперь я понимаю, что потоки запускаются не в каком-то конкретном порядке, поэтому давайте просто предположим, что приведенные выше строки могут появляться в любом порядке.

Но это не то, что происходит.Вместо этого происходит следующее:

Thread 3
Thread 4
Thread 4
Thread 4
Thread 4

или что-то подобное, что заставляет меня поверить, что вместо передачи значения, если i, передается ссылка.(Что странно, поскольку int является типом значения).

Делать что-то вроде этого:

for (int i = 0; i < 5; i++)
  (new Thread(new ThreadStart(delegate()
    {
      int j = i;
      Console.WriteLine("Thread " + j);
    }))).Start();

тоже не помогает, хотя мы сделали копию i.Я предполагаю, что причина в том, что он не сделал копию i вовремя.

Делая что-то вроде этого:

for (int i = 0; i < 5; i++)
{
  (new Thread(new ThreadStart(delegate()
    {
      Console.WriteLine("Thread " + i);
    }))).Start();
  Thread.Sleep(50);
}

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

Вот пример моей текущей конкретной проблемы:

Thread t = new Thread(new ThreadStart(delgate()
  {
    threadLogic(param1, param2, param3, param4);
  }));
t.Start();

param1 = param2 = param3 = param4 = null;

с:

void threadLogic(object param1, object param2, object param3, object param4)
{
  // Do some stuff here...
}

Я хочу, чтобы threadLogic () выполнялся в своем собственном потоке, однако приведенный выше код дает исключение нулевой ссылки.Я предполагаю, что это потому, что значения установлены в ноль, прежде чем поток сможет начать.

Опять же, установка Thread.Sleep (100) работает, но это ужасное решение с любой стороны.Что вы, ребята, рекомендуете для этого конкретного типа гонки?

Ответы [ 2 ]

3 голосов
/ 09 февраля 2011

Вам необходимо ввести временное:

for (int i = 0; i < 5; i++)
{
    int temp = i; // Add this
   (new Thread(new ThreadStart(delegate()
   {
      Console.WriteLine("Thread " + temp);
   }))).Start();
}

Проблема в том, как делегаты закрываются вокруг внешней переменной (i в вашем коде, temp в моем). Область действия неверна (вне цикла for), поэтому к моменту запуска потока значение i уже было увеличено почти, если не полностью.


Для вашего второго примера вам нужно сделать то же самое. Просто сделайте временные:

var temp1 = param1;
var temp2 = param2;
var temp3 = param3;
var temp4 = param4;
Thread t = new Thread(new ThreadStart(delgate()
  {
    threadLogic(temp1, temp2, temp3, temp4);
  }));
t.Start();

// This is now safe, since the closure above is over "temp*"
param1 = param2 = param3 = param4 = null; 
3 голосов
/ 09 февраля 2011

Ваша проблема такая же; это не сам лямбда-синтаксис, а тот факт, что вы закрываете локальную переменную в анонимном методе (используемый вами синтаксис delegate был первой итерацией анонимных методов, которая впервые появилась в .NET 2.0) .

Если вы хотите сделать это, вам придется использовать обходной путь:

for (int i = 0; i < 5; i++)
{
    int j = i;
  (new Thread(new ThreadStart(delegate()
    {
      Console.WriteLine("Thread " + j);
    }))).Start();
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...