2017-01-02 8 views
2

Рассмотрим этот код (извлеченный из Simple-Web-Server, но знание библиотеки не должно быть необходимым, чтобы ответить на этот вопрос):Здесь нужны заграждения?

HttpServer server; 
thread server_thread; 

server.config.port = 8080; 
server.default_resource["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) { 
    string content = "Hello world!" 
    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.size() << "\r\n\r\n" << content; 
}; 

server_thread = thread([&server]() { 
    server.start(); 
}); 

HttpServer::default_resource является станд :: unordered_map, который, в моем понимании, не является потокобезопасный. port - неподписанный короткий.

Предполагая, что мое понимание C++ заборов памяти является правильным, server, как видно по новой нити, не может быть в допустимом состоянии, как основной поток не мог бы написать изменения в port и default_resource память, доступной из других потоков , Таким образом, server.start() может работать неправильно.

Чтобы это исправить, я бы изменить код, добавив к atomic_thread_fence с:

HttpServer server; 
thread server_thread; 

server.config.port = 8080; 
server.default_resource["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) { 
    string content = "Hello world!" 
    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.size() << "\r\n\r\n" << content; 
}; 

atomic_thread_fence(memory_order_release); 

server_thread = thread([&server]() { 
    atomic_thread_fence(memory_order_acquire); 
    server.start(); 
}); 

ли мое понимание правильно, и как atomic_thread_fence s необходимо?

+3

Все операции, выполняемые родительским потоком, упорядоченным до инициализации потока, должны быть завершены с точки зрения только что созданного потока. Поэтому в этом случае забор нити не требуется, поскольку сервер был инициализирован перед вызовом 'std :: thread :: thread()'. –

+0

Кажется, что вы путаете фактический поток (концепция параллелизма) с объектом обработчика потока (объект C++). –

ответ

5

30.3.1.2 нить Конструкторы

template <class F, class ...Args> 
explicit thread(F&& f, Args&&... args); 

Синхронизация: Завершение вызова конструктора синхронизируется с началом вызова копии f.

Другими словами: когда функция потока получает вызывается, она синхронизируется со всем, что произошло в родительском вверните вплоть до std::thread получает построен, в родительском потоке.

Никаких явных барьеров памяти или подобных ограждений такого типа не требуется.

+0

Не работает ли код OPs только потому, что 'server' передается как аргумент, и в этот момент должен был быть запущен весь предыдущий код с побочными эффектами, которые повлияли на эту переменную? Предположим, например, что 'server' не был передан как аргумент, но вместо этого был глобальным - если я правильно понял, то это вызвало бы проблемы синхронизации. В цитируемом тексте просто сказано, что конструктор потока должен быть выполнен до обратного вызова потока. – Lundin

+0

Забор памяти не требуется, даже если вместо std :: thread используется pthread_create(). Эмпирическое правило: создание потока или ожидание другого потока с использованием мьютексов или других примитивов синхронизации неявно выдает требуемые ограждения. Вам не обязательно делать это самостоятельно. К сожалению, я не могу предоставить доказательную ссылку для этого. –

+0

Будет ли то же самое при присоединении к потоку, т. Е. Явные заграждения не требуются после вызова thread :: join()? – Bernard

 Смежные вопросы

  • Нет связанных вопросов^_^