Относительно передачи локальной переменной в потоке - PullRequest
0 голосов
/ 26 декабря 2011

Мне трудно понять неожиданный вывод для следующей программы:

class ThreadTest
{
     static void Main()
     {
          for(int i = 0; i < 10; i++)
               new Thread(() => Console.Write(i)).Start();
     }

}

Запросы: Разный код, запущенный в другом потоке, имеет отдельные стеки? Если да, то чем переменные должны сохранять свои значения, так как int является типом значения?

Ответы [ 2 ]

3 голосов
/ 26 декабря 2011

Каждый поток получает свой собственный стек.Проблема, с которой вы столкнулись, не имеет ничего общего со стеком.Проблема в том, как он генерирует код для вашего анонимного делегата.Используйте инструмент вроде рефлектора, чтобы понять код, который он генерирует.Следующее исправит вашу проблему:

static void Main() 
        {
            for (int i = 0; i < 10; i++)
            {
                int capture = i;
                new Thread(() => Console.Write(capture)).Start();
            }
        } 

Под капотом

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

class SomeClass
{
    public int i { get; set; }

    public void Write()
    {
        Console.WriteLine(i);
    }
}

Компилятор переписывает ваш код следующим образом:

SomeClass someObj = new SomeClass();
for (int i = 0; i < 10; i++)
{
    someObj.i = i;
    new Thread(someObj.Write).Start();
}

и, следовательно, проблема - с которой вы столкнулись.Когда вы захватываете переменную, компилятор делает следующее:

for (int i = 0; i < 10; i++)
{
    SomeClass someObj = new SomeClass();
    someObj.i = i;
    new Thread(someObj.Write).Start();
}

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

Надеюсь, приведенное выше объяснение прояснит ваши сомнения.

Спасибо

2 голосов
/ 26 декабря 2011

Да, потоки имеют свои собственные стеки.Но здесь у вас также есть проблема с захватом переменных.Попробуйте изменить код на:

class ThreadTest
{
    static void Main()
    {
         for(int i = 0; i < 10; i++)
         {
              int j = i;
              new Thread(() => Console.Write(j)).Start();
         }
    }
} 

Заметили изменение в выводе?Каждый поток запускается со ссылкой на переменную, а не на значение.Когда я вставляю строку int j = i;, мы нарушаем захват переменной.Ваш неожиданный вывод связан не столько с потоками, сколько с замыканиями.

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