c - изменчивая переменная кучи в многопоточном приложении [дубликат]

У меня была та же проблема с NetBeans 8.1 в Linux Mint 17.2, и я нашел решение (во время установки выбрал неправильный каталог JDK).

Если не удается найти совместимую установку JDK, вам может понадобиться чтобы вручную добавить путь к каталогу установки JDK, выполнив следующее:

  1. Откройте файл netbeans.conf, расположенный в каталоге netbeans / etc в текстовом редакторе.
  2. Введите местоположение совместимой установки JDK для опции netbeans_jdkhome. Место по умолчанию в Windows - C: \ Program Files \ Java \ jdk1.7.0_67 или подобное. В моем случае: / usr / lib / jvm / java-8-oracle /
  3. Сохраните файл netbeans.conf и запустите исполняемый файл в каталоге netbeans / bin.

https://netbeans.org/community/releases/81/install.html#install_zip

102
задан Martin 30 December 2010 в 01:28
поделиться

4 ответа

#include <iostream>
#include <thread>
#include <unistd.h>
using namespace std;

bool checkValue = false;

int main()
{
    std::thread writer([&](){
            sleep(2);
            checkValue = true;
            std::cout << "Value of checkValue set to " << checkValue << std::endl;
        });

    std::thread reader([&](){
            while(!checkValue);
        });

    writer.join();
    reader.join();
}

Как только интервьюер, который также считал, что volatile бесполезен, со мной спорил, что оптимизация не вызовет никаких проблем и относится к разным ядрам, имеющим отдельные строки кеша и все это (на самом деле не понимало, что именно он ссылаясь на). Но этот фрагмент кода, скомпилированный с -O3 в g ++ (g ++ -O3 thread.cpp -lpthread), показывает неопределенное поведение. В основном, если значение устанавливается перед проверкой while, оно работает нормально, а если нет, оно переходит в цикл, не утруждая себя извлечением значения (которое фактически было изменено другим потоком). В принципе, я считаю, что значение checkValue получает только один раз в регистр и никогда не проверяется снова при самом высоком уровне оптимизации. Если его набор равен true перед выборкой, он работает нормально, и если он не переходит в цикл. Пожалуйста, исправьте меня, если я ошибаюсь.

-2
ответ дан Anu Siril 19 August 2018 в 05:43
поделиться
  • 1
    Что это связано с volatile? Да, этот код UB - но это UB с volatile. – David Schwartz 11 July 2018 в 07:50

Вам нужна летучая и, возможно, блокировка.

volatile сообщает оптимизатору, что значение может изменяться асинхронно, таким образом

volatile bool flag = false;

while (!flag) {
    /*do something*/
}

будет считывать флаг каждый раз вокруг цикла.

Если вы отключите оптимизацию или сделаете каждую переменную изменчивой, программа будет вести себя одинаково, но медленнее. «Я знаю, что вы, возможно, просто прочитали его и знаете, что он говорит, но если я скажу, прочитайте его, тогда прочитайте его.

Блокировка является частью программы. Итак, кстати, если вы реализуете семафоры, то между прочим они должны быть неустойчивыми. (Не пытайтесь, это сложно, возможно, потребуется небольшой ассемблер или новый атомный материал, и это уже сделано.)

0
ответ дан ctrl-alt-delor 19 August 2018 в 05:43
поделиться
  • 1
    Но разве это не так, и тот же пример в другом ответе, ожидание и, следовательно, что-то, чего следует избегать? Если это надуманный пример, есть ли примеры реальной жизни, которые не надуманны? – David Preston 3 January 2011 в 18:19
  • 2
    Да, хорошо. Вам действительно нужно что-то сделать в фигурных скобках (я отредактирую пост). Занятое ожидание - обычно плохая идея. Вы можете обрабатывать что-то (список), пока другой поток не даст вам остановить. Без шаровидности он будет продолжаться вечно, и для этого примера блокировка не требуется, bool является атомарным. – ctrl-alt-delor 5 January 2011 в 18:54
  • 3
    @Chris: Занятое ожидание иногда является хорошим решением. В частности, если вы ожидаете всего лишь подождать пару тактов, это будет намного меньше накладных расходов, чем гораздо более тяжелый подход к приостановке потока. Конечно, как я уже упоминал в других комментариях, такие примеры, как этот, ошибочны, потому что предполагают, что чтение / запись на флаг не будет переупорядочено в отношении защищаемого кода, и такая гарантия не предоставляется, и поэтому , volatile не очень полезно даже в этом случае. Но ожидание - это иногда полезная техника. – jalf 5 January 2011 в 21:05
  • 4
    @jalf мое понимание вещей заключается в том, что volatile сообщает компилятору, что переменная может быть прочитана / записана асинхронно в программе (другим потоком или моим оборудованием), она должна дать семантику «если я скажу, прочитайте или напишите, затем прочитайте или напиши и сделай это, когда я тебе скажу ». Если CPU переопределяет инструкцию, которая изменяет эту семантику, то компилятор должен также победить эту оптимизацию. Я что-то упускаю. – ctrl-alt-delor 10 January 2015 в 11:48
  • 5
    @richard Да и нет. Первая половина верна. Но это означает только то, что CPU и компилятор не могут переупорядочивать изменчивые переменные по отношению друг к другу. Если я прочитаю изменчивую переменную A, а затем прочитаю изменчивую переменную B, тогда компилятор должен испускать код, который гарантирован (даже с переупорядочением процессора), чтобы читать A до B. Но он не дает никаких гарантий относительно всех нелетучих переменных доступа , Они могут быть переупорядочены вокруг вашего изменчивого чтения / записи. Поэтому, если вы не введете переменную every в свою программу нестабильной, она не даст вам гарантию, – jalf 10 January 2015 в 12:45

Short & amp; быстрый ответ: volatile (почти) бесполезен для платформо-агностического многопоточного программирования приложений. Он не обеспечивает никакой синхронизации, не создает забора памяти и не обеспечивает порядок выполнения операций. Он не делает операции атомарными. Это не делает ваш код волшебным потоком в безопасности. volatile может быть единственным непонятым средством во всех C ++. См. этот , этот и этот для получения дополнительной информации о volatile

С другой стороны, volatile имеют некоторое применение, которое может быть не столь очевидным. Его можно использовать так же, как и использовать const, чтобы помочь компилятору показать вам, где вы можете ошибиться при доступе к некоторому совместно используемому ресурсу незащищенным способом. Это использование обсуждается Александреску в этой статье . Тем не менее, в основном это используется система типа C ++, которая часто рассматривается как средство и может вызывать Undefined Behavior.

volatile специально предназначалось для использования при взаимодействии с аппаратным обеспечением с отображением памяти, обработчиков сигналов и команды машинного кода setjmp. Это делает volatile прямо применимым к программированию на системном уровне, а не к нормальному программированию на уровне приложений.

В стандарте C ++ 2003 не говорится, что volatile применяет любую семантику Acquire или Release для переменных. Фактически, стандарт полностью умалчивает обо всех вопросах многопоточности. Однако определенные платформы действительно применяют семантику Acquire и Release в переменных volatile.

[Обновление для C ++ 11]

Стандарт C ++ 11 теперь делает подтверждают многопоточность непосредственно в модели памяти и lanuage, и она предоставляет библиотечные возможности для решения этой проблемы независимо от платформы. Однако семантика volatile все еще не изменилась. volatile все еще не является механизмом синхронизации. Bjarne Stroustrup говорит так же в TCPPPL4E:

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

Не предполагайте, что volatile имеет особое значение в модели памяти. Это не. Это не так, как в некоторых более поздних языках - механизм синхронизации. Чтобы получить синхронизацию, используйте atomic, mutex или condition_variable.

[/ End update]

Вышеприведенное все применимо к C ++ самого языка, как это определено Стандартом 2003 года (и теперь стандартом 2011 года). Однако некоторые конкретные платформы добавляют дополнительные функциональные возможности или ограничения на то, что делает volatile. Например, в MSVC 2010 (по крайней мере) семантика получения и выпуска применяется к определенным операциям в переменных volatile. Из MSDN :

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

Запись в изменчивый объект (volatile write) имеет семантику Release; ссылка на глобальный или статический объект, который возникает до того, как запись в энергозависимый объект в последовательности команд произойдет до этой изменчивой записи в скомпилированном двоичном файле.

Чтение изменчивого объекта (изменчивое чтение) имеет Получить семантику; ссылка на глобальный или статический объект, который возникает после считывания энергозависимой памяти в последовательности команд, будет возникать после этого изменчивого чтения в скомпилированном двоичном файле.

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

135
ответ дан kaartic 19 August 2018 в 05:43
поделиться
  • 1
    Часть меня хочет понизить это из-за снисходительного тона ответа и первого комментария. «летучий» бесполезен ». сродни к "ручному распределению памяти бесполезно". Если вы можете написать многопоточную программу без volatile, это потому, что вы стояли на плечах людей, которые использовали volatile для реализации библиотек потоков. – Ben Jackson 29 December 2010 в 23:19
  • 2
    @Ben только потому, что что-то бросает вызов вашим убеждениям, не делает его снисходительным – David Heffernan 29 December 2010 в 23:25
  • 3
    @Ben: нет, прочитайте, что volatile на самом деле делает в C ++. Что @John сказал правильный , конец истории. Он не имеет ничего общего с кодом приложения или кодом библиотеки, или «обычным». vs "богоподобные всезнающие программисты" в этом отношении. volatile не требуется и бесполезен для синхронизации между потоками. Библиотеки потоков не могут быть реализованы с точки зрения volatile; он все равно должен полагаться на детали, зависящие от платформы, и когда вы полагаетесь на них, вам больше не нужно volatile. – jalf 30 December 2010 в 00:40
  • 4
    @jalf: "volatile не является необходимым и бесполезным для синхронизации между потоками" (это то, что вы сказали) - это не то же самое, что «volatile бесполезно для многопоточного программирования». (это то, что сказал Джон в ответ). Вы на 100% правильны, но я не согласен с Джоном (частично) - volatile все еще может использоваться для многопоточного программирования (для очень ограниченного набора задач) – Dan 12 February 2011 в 20:31
  • 5
    @GMan: все, что полезно, полезно только при определенном наборе требований или условий. Волатильность полезна для многопоточного программирования под строгим набором условий (а в некоторых случаях может быть даже лучше (для некоторого определения лучше), чем альтернативы). Вы говорите, «игнорируя это, и ...». но случай, когда volatile полезен для многопоточности, ничего не игнорирует. Вы составили то, что я никогда не утверждал. Да, полезность волатильности ограничена, но она существует, но мы все можем согласиться с тем, что она НЕ полезна для синхронизации. – Dan 21 May 2011 в 20:34

Летучие иногда полезны по следующей причине: этот код:

/* global */ bool flag = false;

while (!flag) {}

оптимизирован gcc:

if (!flag) { while (true) {} }

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

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

26
ответ дан zeuxcg 19 August 2018 в 05:43
поделиться
  • 1
    Если я помню, C ++ 0x атомный, предназначен для правильного выполнения того, что многие считают (ошибочно) неверными. – David Heffernan 29 December 2010 в 22:33
  • 2
    volatile не препятствует переупорядочению доступа к памяти. volatile доступа не будут переупорядочены по отношению друг к другу, но они обеспечивают no гарантию о переупорядочении по отношению к объектам, отличным от volatile, и поэтому они в основном бесполезны в качестве флагов, поскольку Что ж. – jalf 30 December 2010 в 00:42
  • 3
    @Ben: Я думаю, у вас есть это с ног на голову. «Летучий» бесполезен ». толпа полагается на простой факт, что volatile не защищает от переупорядочения , что означает, что он абсолютно бесполезен для синхронизации. Другие подходы могут быть в равной степени бесполезны (как вы заметили, оптимизация кода ссылок может позволить компилятору заглянуть в код, который, по вашему мнению, компилятор будет рассматривать как черный ящик), но это не устраняет недостатки volatile. – jalf 5 January 2011 в 21:02
  • 4
    @jalf: См. статью Арча Робинсона (ссылка на которую находится на этой странице), 10-й комментарий («Spud»). В принципе, переупорядочение не изменяет логику кода. В опубликованном коде используется флаг для отмены задачи (а не для того, чтобы сигнализировать о выполнении задачи), поэтому не имеет значения, отменена ли задача до или после кода (например: while (work_left) { do_piece_of_work(); if (cancel) break;}, если отмена переупорядочена в пределах цикл, логика по-прежнему действительна. У меня был фрагмент кода, который работал аналогично: если основной поток хочет завершить работу, он устанавливает флаг для других потоков, но это не так ... – Dan 13 February 2011 в 15:03
  • 5
    ... имеет значение, если другие потоки выполняют несколько итераций своих рабочих циклов до их завершения, если это происходит достаточно быстро после установки флага. Конечно, это ТОЛЬКО использование, о котором я могу думать, и его довольно ниша (и может не работать на платформах, где запись в изменчивую переменную не делает изменения видимыми для других потоков, хотя по крайней мере на x86 и x86-64 это работает). Я, конечно, не советовал бы кому-либо на самом деле делать это без уважительной причины, я просто говорю, что общий оператор, такой как «volatile, никогда не полезен в многопоточном коде», не на 100% правильнее. – Dan 13 February 2011 в 15:06
Другие вопросы по тегам:

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