Это дополнительный вопрос Shared_ptr и видимость памяти в c ++ и Создание объекта в потоке A, использование в потоке B. Требуется мьютекс? .
Этот вопрос больше касается видимости памяти, а не гонки данных.
В Java у меня есть:
ExecutorService executor = Executors.newSingleThreadExecutor();
Integer i = new Integer(5); // no write to i afterwards
executor.submit(() -> {
System.out.println(i);
});
Я не думаю, что это потокобезопасно.Поскольку нет необходимости помещать значение 5 в основную память, оно может оставаться в кэше ЦП основного потока.Поскольку нет барьера памяти, потоку исполнителя не гарантируется увидеть значение 5. Чтобы убедиться, что значение находится в основной памяти, вы либо используете синхронизацию, либо используете AtomicInteger
, либо volatile int.
Если вы делаете что-то подобное с shared_ptr
в C ++, это безопасно?
auto sp = std::make_shared<int>(5); // no write to the int afterwards
myExecutor.submit([sp](){
std::cout << sp;
});
Поток исполнителя гарантированно увидит значение 5?Обратите внимание, что shared_ptr
копируется в лямбду, а не в int.
Вот более полный пример:
Предположим, у меня есть основной поток и рабочий поток.В основном потоке я создал shared_ptr<Object>
и скопировал shared_ptr
в рабочий поток. Безопасно ли использовать копию shared_ptr
, если вообще нет синхронизации в классе Object (НЕТ записи в объект?после постройки)?
Моя главная загадка состоит в том, что Объект создается в главном потоке в куче, shared_ptr
копируется, но не Объект.Будет ли рабочий поток определенно иметь видимость объекта в памяти?Возможно ли, что значение Object на самом деле находится в кэше ЦП основного потока, а не в основной памяти?
struct WorkingQueue{
WorkingQueue()=default;
void push(std::function<void()> task){
std::lock_guard<std::mutex> lock{mutex};
queue.push(std::move(task));
}
std::optional<std::function<void()>> popIfNotEmpty(){
std::lock_guard<std::mutex> lock{mutex};
if(queue.empty()){
return std::nullopt;
}
auto task = queue.front();
queue.pop();
return task;
}
bool empty(){
std::lock_guard<std::mutex> lock{mutex};
return queue.empty();
}
mutable std::mutex mutex;
std::queue<std::function<void()>> queue;
};
int main(){
WorkingQueue queue;
std::atomic<bool> stopFlag{false};
auto f = std::async(std::launch::async, [&queue, &stopFlag](){
while(!stopFlag || !queue.empty()){
auto task = queue.popIfNotEmpty();
if(task){
(*task)();
}
}
});
auto sp = std::make_shared<int>(5);
queue.push([sp](){
std::cout << *sp;
});
stopFlag = true;
f.get();
}
Гарантирован ли этот программист для вывода 5?