Различное поведение при запуске потока: ParameterizedThreadStart и Anonymous Delegate. Почему это имеет значение? - PullRequest
5 голосов
/ 17 декабря 2009

Когда я запускаю код ниже, вывод «DelegateDisplayIt», как правило, повторяется 1-4 раза. Я запускал этот код, вероятно, 100 раз, и ни разу не получилось «ParameterizedDisplayIt». Таким образом, похоже, что способ создания и последующего запуска потока влияет на способ передачи параметра. При создании нового потока с анонимным делегатом параметр является ссылкой на исходную переменную, но при создании с делегатом ParameterizedThreadStart параметр является совершенно новым объектом? Мое предположение кажется правильным? Если так, то это кажется странным побочным эффектом конструктора потока, нет?

static void Main()
{
    for (int i = 0; i < 10; i++)
    {
        bool flag = false;

        new Thread(delegate() { DelegateDisplayIt(flag); }).Start();

        var parameterizedThread = new Thread(ParameterizedDisplayIt);
        parameterizedThread.Start(flag);

        flag = true;
    }

    Console.ReadKey();
}

private static void DelegateDisplayIt(object flag)
{
    if ((bool)flag)
        Console.WriteLine("DelegateDisplayIt");
}

private static void ParameterizedDisplayIt(object flag)
{
    if ((bool)flag)
        Console.WriteLine("ParameterizedDisplayIt");
}

Ответы [ 3 ]

5 голосов
/ 17 декабря 2009

Ваше предположение верно. Оператор parameterizedThread.Start(flag) копирует переменную flag во время вызова.

Напротив, анонимный делегат фиксирует исходную переменную в замыкании . Переменная не копируется, пока делегат не выполнит метод DelegateDisplayIt. В этот момент значение может быть истинным или ложным, в зависимости от того, где находится исходный поток в вызывающем цикле.

4 голосов
/ 17 декабря 2009

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

Если вы используете анонимного делегата, вы неявно будете использовать замыкание для доступа к логическому значению.

В .NET компилятор создает анонимный тип для хранения ссылки на переменную, которая является предметом замыкания ( flag ), на которую затем ссылаются как анонимный делегат, так и метод. Затем они поделятся переменной, так что если кто-то изменит ее, оба увидят изменение.

Это действительно не многопоточный вопрос, он касается передачи по значению и замыканий. Вот достойный пост в блоге о замыканиях . Передача по значению довольно проста; если вам нужно это освежить, я бы предложил купить копию CLR Via C # Джеффри Рихтера.

1 голос
/ 17 декабря 2009

Давайте рассмотрим первый случай:

for (int i = 0; i < 10; i++)
{
    bool flag = false;
    new Thread(delegate() { DelegateDisplayIt(flag); }).Start();
    flag = true;
}

Здесь, когда вы создаете анонимный делегат, значение флага равно false, но когда выполняется метод DelegateDisplayIt, флаг уже установил значение true в следующей строке, и вы увидите вывод результатов. Вот еще один пример, который иллюстрирует ту же концепцию:

for (int i = 0; i < 5; i++) 
{
    ThreadPool.QueueUserWorkItem(state => Console.WriteLine(i));
}

Это напечатает пять раз пять.

Теперь давайте возьмем второй случай:

for (int i = 0; i < 10; i++)
{
    bool flag = false;
    var parameterizedThread = new Thread(ParameterizedDisplayIt);
    parameterizedThread.Start(flag);        
    flag = true;
}

значение, передаваемое обратному вызову, является значением, которое переменная имеет при вызове метода Start, то есть false, и поэтому вы никогда не видите вывод в консоли.

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