Должен ли std:: future:: wait использовать столько CPU? Есть ли более эффективный вызов?
EDIT: tl; dr - эта проблема, по-видимому, ограничена небольшим набором комбинаций OS/компилятор/библиотека и теперь отслеживается в GCC Bugzilla как Ошибка 68921 благодаря @JonathanWakely.
Я жду в будущем, и я заметил, что top
показывает 100% использование ЦП и strace
показывает постоянный поток вызовов futex
:
...
[pid 15141] futex(0x9d19a24, FUTEX_WAIT, -2147483648, {4222429828, 3077922816}) = -1 EINVAL (Invalid argument)
...
Это на Linux 4.2.0 (32-разрядный i686
), скомпилированный с версией gcc версии 5.2.1.
Вот моя минимально жизнеспособная примерная программа:
#include <future>
#include <iostream>
#include <thread>
#include <unistd.h>
int main() {
std::promise<void> p;
auto f = p.get_future();
std::thread t([&p](){
std::cout << "Biding my time in a thread.\n";
sleep(10);
p.set_value();
});
std::cout << "Waiting.\n";
f.wait();
std::cout << "Done.\n";
t.join();
return 0;
}
и вот вызов компилятора (такое же поведение без -g
):
g++ --std=c++11 -Wall -g -o spin-wait spin-wait.cc -pthread
Есть ли более эффективная альтернатива?
Вот логически-подобная программа, использующая std::condition_variable
, которая выглядит намного лучше:
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
#include <unistd.h>
int main() {
bool done = 0;
std::mutex m;
std::condition_variable cv;
std::thread t([&m, &cv, &done](){
std::cout << "Biding my time in a thread.\n";
sleep(10);
{
std::lock_guard<std::mutex> lock(m);
done = 1;
}
cv.notify_all();
});
std::cout << "Waiting.\n";
{
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&done]{ return done; });
}
std::cout << "Done.\n";
t.join();
return 0;
}
Я делаю что-то не так с моим кодом std::future
, или это реализация в моем libstdc++
просто так плохо?
Ответы
Ответ 1
Нет, конечно, это не должно быть сделано, это ошибка в реализации, а не свойство std::future
.
Теперь это https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68921 - цикл, который вызывает вызов futex(2)
, находится в __atomic_futex_unsigned::_M_load_and_test_until
Он выглядит как простой отсутствующий аргумент функции syscall
, поэтому указатель мусора передается ядру, который жалуется, что он не является допустимым аргументом timespec*
. Я тестирую исправление и буду совершать завтра, поэтому он будет исправлен в GCC 5.4
Ответ 2
Нет, не следует. Обычно он отлично работает.
(В комментариях мы пытаемся определить больше о конкретной сломанной конфигурации, в которой результирующий исполняемый файл, похоже, затягивается, но я считаю, что ответ. Было бы неплохо определить, является ли это еще spin-wait на 32-битной цели в последнем g++.)
promise - это "толчок" будущего канала связи будущего будущего: операция, которая хранит значение в общем состоянии синхронизируется с (как определено в std::memory_order
) успешным возвратом из любой функции, ожидающей в общем состоянии (например, std::future::get
).
Я предполагаю, что это включает std::future::wait
.
[ std::promise::set_value
] Атомно сохраняет значение в общем состоянии и готовит состояние. Операция ведет себя так, как будто set_value
, set_exception
, set_value_at_thread_exit
и set_exception_at_thread_exit
приобретают один мьютекс, связанный с объектом обещания, при обновлении объекта обещания.
Хотя это немного тревожит то, что они описывают синхронизацию с точки зрения объекта -обещающего, а не общего состояния, намерение довольно ясно.
cppreference.com [*] продолжает использовать его точно так, как он не работал в вопросе выше. ( "Этот пример показывает, как обетование можно использовать в качестве сигналов между потоками".)