Я слышал, что i ++ не потокобезопасен, ++ я потокобезопасен? - PullRequest
88 голосов
/ 25 марта 2009

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

Тем не менее, мне интересно о ++ я. Насколько я могу судить, это сводится к одной инструкции сборки, такой как 'add r1, r1, 1', и, поскольку это всего лишь одна команда, она будет бесперебойной при переключении контекста.

Кто-нибудь может уточнить? Я предполагаю, что используется платформа x86.

Ответы [ 15 ]

1 голос
/ 25 марта 2009

Стандарт C ++ 1998 года не имеет ничего общего с потоками, хотя следующий стандарт (который должен быть выпущен в этом году или в следующем) - нет. Таким образом, вы не можете сказать что-нибудь умное о поточной безопасности операций, не обращаясь к реализации. Это не просто используемый процессор, а комбинация компилятора, ОС и модели потоков.

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

Ничто не является поточно-ориентированным, если у вас нет документации, в которой говорится, что это для конкретной системы, которую вы используете.

0 голосов
/ 02 сентября 2009

Брось я в нить локальное хранилище; это не атомарно, но тогда это не имеет значения.

0 голосов
/ 10 июня 2009

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

Вот это на Java:

public class IntCompareAndSwap {
    private int value = 0;

    public synchronized int get(){return value;}

    public synchronized int compareAndSwap(int p_expectedValue, int p_newValue){
        int oldValue = value;

        if (oldValue == p_expectedValue)
            value = p_newValue;

        return oldValue;
    }
}

public class IntCASCounter {

    public IntCASCounter(){
        m_value = new IntCompareAndSwap();
    }

    private IntCompareAndSwap m_value;

    public int getValue(){return m_value.get();}

    public void increment(){
        int temp;
        do {
            temp = m_value.get();
        } while (temp != m_value.compareAndSwap(temp, temp + 1));

    }

    public void decrement(){
        int temp;
        do {
            temp = m_value.get();
        } while (temp > 0 && temp != m_value.compareAndSwap(temp, temp - 1));

    }
}
0 голосов
/ 22 мая 2009

Я думаю, что если выражение «i ++» является единственным в выражении, оно эквивалентно «++ i», компилятор достаточно умен, чтобы не сохранять временное значение и т. Д. Поэтому, если вы можете использовать их взаимозаменяемо ( в противном случае вы не будете спрашивать, какой из них использовать), не имеет значения, какой бы вы ни использовали, поскольку они почти одинаковы (за исключением эстетики).

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

Если вы хотите поэкспериментировать самостоятельно, напишите программу, в которой N потоков одновременно увеличивают общую переменную M раз каждая ... если значение меньше N * M, то некоторое приращение было перезаписано. Попробуйте это как с преинкрементом, так и с постинкрементом и сообщите нам; -)

0 голосов
/ 25 марта 2009

Вы говорите: «Это всего лишь одна инструкция, она не будет прервана переключением контекста». - это все хорошо для одного процессора, но как насчет двухъядерного процессора? Тогда у вас может быть два потока, одновременно обращающихся к одной и той же переменной без каких-либо переключений контекста.

Не зная языка, ответ - проверить его.

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