Локальные переменные статического метода и потокобезопасность

С нормальными методами экземпляра локальные переменные ориентированы на многопотоковое исполнение.

Если у меня есть следующее в статическом методе:

int i = 0;
i += 3;

Это было бы ориентировано на многопотоковое исполнение? Есть ли какая-либо выгода?

Кроме того, что точно это означает, когда каждая переменная имеет свой собственный стек? Это означает его собственный stacktrace?

Спасибо

9
задан user 1 August 2012 в 02:49
поделиться

4 ответа

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

Уловка возникает, если вы пытаетесь разделить значение i между потоками, например, сделав i статическим полем. Тогда можно получить состояние гонки, в котором вы получите разные результаты в зависимости от времени.

Что касается вашего второго вопроса, каждый поток имеет свой собственный стек, а не каждую переменную.

15
ответ дан 4 December 2019 в 07:47
поделиться

Во-первых, я предполагаю, что под «потокобезопасностью» вы подразумеваете «вести себя так, как будто определенные операции являются атомарными, когда мутации происходят в нескольких потоках». Если это не то, что вы подразумеваете под «потокобезопасным», пожалуйста, внимательно определите «потокобезопасный», прежде чем задавать вопросы по этому поводу. Кажется, что почти каждый, кто задает вопросы о безопасности потоков при переполнении стека, имеет другое личное определение того, что это означает.

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

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

Кроме того, что именно означает, когда каждая переменная имеет свой собственный стек?

Я понятия не имею, что это значит; переменные не имеют стеков. Потоки имеют стопки.

7
ответ дан 4 December 2019 в 07:47
поделиться

Короткий ответ: да, следующий метод потокобезопасен:

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 не имеют общей памяти, поэтому он потокобезопасен.

Потоки не разделяют свои кадры стека. Они делят кучу.

6
ответ дан 4 December 2019 в 07:47
поделиться

Вот демонстрация с (упрощенным) примером, чтобы помочь расширить сказанное Марком:

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

0
ответ дан 4 December 2019 в 07:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: