С нормальными методами экземпляра локальные переменные ориентированы на многопотоковое исполнение.
Если у меня есть следующее в статическом методе:
int i = 0;
i += 3;
Это было бы ориентировано на многопотоковое исполнение? Есть ли какая-либо выгода?
Кроме того, что точно это означает, когда каждая переменная имеет свой собственный стек? Это означает его собственный stacktrace?
Спасибо
Если отправленные вами строки находятся внутри метода, то нет причин, по которым он не должен быть потокобезопасным. Потоки никак не взаимодействуют - каждый поток видит свой i
.
Уловка возникает, если вы пытаетесь разделить значение i между потоками, например, сделав i
статическим полем. Тогда можно получить состояние гонки, в котором вы получите разные результаты в зависимости от времени.
Что касается вашего второго вопроса, каждый поток имеет свой собственный стек, а не каждую переменную.
Во-первых, я предполагаю, что под «потокобезопасностью» вы подразумеваете «вести себя так, как будто определенные операции являются атомарными, когда мутации происходят в нескольких потоках». Если это не то, что вы подразумеваете под «потокобезопасным», пожалуйста, внимательно определите «потокобезопасный», прежде чем задавать вопросы по этому поводу. Кажется, что почти каждый, кто задает вопросы о безопасности потоков при переполнении стека, имеет другое личное определение того, что это означает.
Во-вторых, локальные переменные небезопасны . В частности, локальные переменные, которые являются закрытыми внешними переменными лямбда- или анонимного метода или которые находятся внутри блока итератора, не гарантируют поточно-безопасную работу при изменении в нескольких потоках.
Локальные переменные, которые не являются закрытыми внешними переменными анонимной функции и не находятся в блоке итератора, могут изменяться только текущим потоком и поэтому защищены от изменения сразу несколькими потоками.
Кроме того, что именно означает, когда каждая переменная имеет свой собственный стек?
Я понятия не имею, что это значит; переменные не имеют стеков. Потоки имеют стопки.
Короткий ответ: да, следующий метод потокобезопасен:
public static int CalcuateSomething( int number ) {
int i = number * 10;
return i;
}
Все локальные переменные безопасны, если они не указывают на общий объект в куче .
Как и в случае однопоточных приложений, следующие вызовы этого метода будут возвращать разные значения.
CalculateSomething( 10 ) => 100
CalculateSomething( 20 ) => 200
Почему? Потому что каждый вызов номера метода принимает разные значения, и поэтому я тоже буду. Значение i не запоминается после завершения функции, потому что i размещается в стеке. Почти во всех языках функции моделируются в стеке. Каждый раз, когда вы вызываете другой метод, текущий метод приостанавливается. Новый метод помещается в стек вызовов и вызывается. Когда этот метод завершается, программа возобновляет выполнение вызывающего метода с того места, где он остановился (то есть с returnAddress). Любая локальная переменная, определенная в этом методе, является частью стека этого метода. Стековый фрейм для нашего метода, описанного выше, можно представить так:
public Class StackFrameForCalculateSomething implements StackFrame {
public int ReturnAddress;
public int i = 0;
public int number;
}
Стек можно представить как коллекцию объектов StackFrame.
Stack callStack
Каждый раз, когда вызывается новый метод, программа может делать что-то вроде следующего:
StackFrameForCalculcateSomething s = new StackFrameForCalculateSomething();
s.returnAddress = instructionPointer;
s.number = 10;
callStack.push( s );
s.invoke();
StackFrameForCalculcateSomething s2 = new StackFrameForCalculateSomething();
s2.returnAddress = instructionPointer;
s2.number = 20;
callStack.push( s2 );
s2.invoke();
Что это означает для потоковой передачи? Что ж, в случае потоков у вас будет несколько независимых callStacks с их собственной коллекцией. Причина, по которой доступ к локальным переменным безопасен, заключается в том, что callStack потока 1 не может получить доступ к callStack потока 2, потому что они являются отдельными объектами. Как и в случае с одним потоком, s и s2 - это разные объекты с разными значениями числа. И поэтому они независимы друг от друга. Предположим, что s был в потоке 1, а s2 - это поток 2. Поток 1 и поток 2 не имеют общей памяти, поэтому он потокобезопасен.
Потоки не разделяют свои кадры стека. Они делят кучу.
Вот демонстрация с (упрощенным) примером, чтобы помочь расширить сказанное Марком:
void Main()
{
for (int x = 0; x < 10; x++)
{
ThreadPool.QueueUserWorkItem(z=> myClass.DoSomething());
}
}
public class myClass
{
public static void DoSomething()
{
int i = 0;
Console.WriteLine (i += 3);
}
}
Результат: 3 3 3 3 3 3 3 3 3 3 3