Подтвердить что ты не робот

Многопоточность и закрытие в .NET.

Если у меня есть это:

public string DoSomething(string arg)
{
    string someVar = arg;
    DoStuffThatMightTakeAWhile();
    return SomeControl.Invoke(new Func<string>(() => someVar));
}

И этот метод можно вызывать одновременно из нескольких потоков, а один поток застревает в DoStuffThatMightTakeAWhile, а затем второй поток вызывает DoSomething с другим arg, это изменит значение someVar для все потоки и, следовательно, DoSomething возвращают вторую версию someArg для обоих вызовов или один someVar существует для каждого потока?

Изменить Я думаю, что мой Action должен был отредактировать Func.

4b9b3361

Ответ 1

В ответах есть несколько недоразумений, в основном основанных на неправде, что локальные переменные выделяются "в стеке потока". Это и ложно, и не имеет значения.

Это false, потому что локальная переменная может быть выделена в некотором временном пуле или в пуле длительного хранения; даже если он распределен во временном пуле, который не обязательно должен быть стекю; это может быть регистр. Это не имеет значения, потому что кто заботится о том, на каком пуле хранится хранилище?

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

Итак, рассмотрим ваш вопрос:

Этот метод можно вызывать одновременно из нескольких потоков. Если один поток застрял в DoStuffThatMightTakeAWhile, а затем второй поток вызывает DoSomething с другим arg, это изменит значение someVar для всех потоков?

Нет. Для каждой активации DoSomething существует новый "someVar".

будет ли существовать какой-нибудь символ для каждого потока?

Для каждой активации будет существовать один "someVar". Если поток выполняет ровно одну активацию, тогда будет ровно один someVar на поток; если поток выполняет миллион активаций, тогда их будет миллион.

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

Ответ 2

Локальные переменные, хранящиеся в текущем потоковом стеке, поэтому каждый поток будет иметь свой собственный стек и каждую собственную переменную someVar.

Так как это были бы разные значения в каждом потоке

new Action(() => someVar)); 

будет записывать собственное значение someVar.

Edit

Я просто ошибся, сказав это, как указал Эрик. См. Его ответ для правильного объяснения.

Ответ 3

Как сказано, каждый поток, ударяющий DoSomething, создает отдельный стек someVar в своем стеке, и поэтому нить не влияет на другой someVar.

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

public static void Main(string[] args)
{
    int x = 0;
    new Thread(() => {while(x != 100){Console.WriteLine(x);}}).Start();
    for(int i = 0; i != 100; ++i)
    {
        x = i;
        Thread.Sleep(10);
    }
    x = 100;
    Console.ReadLine();
}

Демонстрирует это.