if (cmd.type == open_new_document) {

   std::string const new_name = get_filename_from_user();

   std::thread t(edit_document,new_name); ←(1)

   t.detach(); ←(2)

  }

  else {

   process_user_input(cmd);

  }

 }

}

Когда пользователь открывает новый документ, мы спрашиваем, какой документ открыть, затем запускаем поток, в котором этот документ открывается (1), и отсоединяем его (2). Поскольку новый поток делает то же самое, что текущий, только с другим файлом, то мы можем использовать ту же функцию (edit_document), передав ей в качестве аргумента имя только что выбранного файла.

Этот пример демонстрирует также, почему бывает полезно передавать аргументы функции потока: мы передаем конструктору объекта std::thread не только имя функции (1), но и её параметр — имя файла. Существуют другие способы добиться той же цели, например, использовать не обычную функцию с параметрами, а объект-функцию с данными-членами, но библиотека предлагает и такой простой механизм.

<p>2.2. Передача аргументов функции потока</p>

Из листинга 2.4 видно, что по существу передача аргументов вызываемому объекту или функции сводится просто к передаче дополнительных аргументов конструктору std::thread. Однако важно иметь в виду, что по умолчанию эти аргументы копируются в память объекта, где они доступны вновь созданному потоку, причем так происходит даже в том случае, когда функция ожидает на месте соответствующего параметра ссылку. Вот простой пример:

void f(int i, std::string const& s);

std::thread t(f, 3, "hello");

Здесь создается новый ассоциированный с объектом t поток, в котором вызывается функция f(3, "hello"). Отметим, что функция f принимает в качестве второго параметра объект типа std::string, но мы передаем строковый литерал char const*, который преобразуется к типу std::string уже в контексте нового потока. Это особенно важно, когда переданный аргумент является указателем на автоматическую переменную, как в примере ниже:

void f(int i, std::string const& s);

void oops(int some_param) {

 char buffer[1024];           ←(1)

 sprintf(buffer, "%i", some_param);

 std::thread t(f, 3, buffer);(2)

 t.detach();

}

В данном случае в новый поток передается (2) указатель на локальную переменную buffer (1), и есть все шансы, что выход из функции oops произойдет раньше, чем буфер будет преобразован к типу std::string в новом потоке. В таком случае мы получим неопределенное поведение. Решение заключается в том, чтобы выполнить преобразование в std::string до передачи buffer конструктору std::thread:

void f(int i,std::string const& s);

void not_oops(int some_param) {

 char buffer[1024];                         │Использование

 sprintf(buffer, "%i", some_param);         │std::string

 std::thread t(f, 3, std::string(buffer)); ←┘позволяет избежать

 t.detach();                                  висячего указателя

}

В данном случае проблема была в том, что мы положились на неявное преобразование указателя на buffer к ожидаемому типу первого параметра std::string, а конструктор std::thread копирует переданные значения «как есть», без преобразования к ожидаемому типу аргумента.

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

void update_data_for_widget(widget_id w,widget_data& data); ←(1)

void oops_again(widget_id w) {

 widget_data data;

 std::thread t(update_data_for_widget, w, data); ←(2)

 display_status();

 t.join();

 process_widget_data(data);                      ←(3)

}

Перейти на страницу:

Похожие книги