многопоточность — метод C ++ в потоке. Разница между передачей: объект, адрес объекта, std :: ref объекта

Я пытаюсь выполнить метод объекта в потоке C ++.

Я могу сделать это, передавая адрес метода и объекта (или адрес объекта, или std :: ref (my_obj)) конструктору потока.

Я заметил, что если я передаю объект, а не адрес объекта или std :: ref (my_obj), то объект копируется дважды (Я печатаю некоторую информацию в конструкторе копирования, чтобы увидеть это).

Вот код:

class Warrior{
string _name;
public:
// constructor
Warrior(string name): _name(name) {}

// copy constructor (prints every time the object is copied)
Warrior(const Warrior & other): _name("Copied " + other._name){
cout << "Copying warrior: \"" << other._name;
cout << "\" into : \"" << _name << "\"" << endl;
}

void attack(int damage){
cout << _name << " is attacking for " << damage << "!" << endl;
}
};

int main(){
Warrior conan("Conan");

// run conan.attack(5) in a separate thread
thread t(&Warrior::attack, conan, 5);
t.join(); // wait for thread to finish

}

Вывод, который я получаю в этом случае

Copying warrior: "Conan" into : "Copied Conan"Copying warrior: "Copied Conan" into : "Copied Copied Conan"Copied Copied Conan is attacking for 5!

Хотя если я просто пройду &conan или же std::ref(conan) в качестве второго аргумента thread t(...) (вместо прохождения conan), вывод просто:

Conan is attacking for 5!

У меня есть 4 сомнения:

  1. Почему у меня есть 2 копии объекта вместо 1?

    Я ожидал, что, передав экземпляр объекта в конструктор потока, объект будет скопирован один раз в собственном стеке потока, а затем attack() метод будет вызван на этой копии.

  2. Какова точная причина, почему конструктор потока может принять объект, адрес или std::ref? Используется ли эта версия конструктора (которую, я признаю, я не до конца понимаю)

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

    во всех 3 случаях?

  3. Если мы исключим первый случай (так как он неэффективен), что я должен использовать между &conan а также std::ref(conan)?

  4. Это как-то связано с синтаксисом, требуемым std::bind?

1

Решение

Почему у меня есть 2 копии объекта вместо 1?

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

Какова точная причина, почему конструктор потока может принимать объект, адрес или std :: ref? Используется ли эта версия конструктора (которую, я признаю, я не до конца понимаю)

std::thread в основном начинается новая тема с вызовом, как

std::invoke(decay_copy(std::forward<Function>(f)),
decay_copy(std::forward<Args>(args))...);

std::invoke Он создан для обработки всех видов вызываемых типов, и один из них — когда он имеет указатель на функцию-член и объект и соответствующим образом вызывает функцию. Это также знает о std::reference_wrapper и может обрабатывать вызов указателя на функцию-член на std::reference_wrapper к объекту.

Если мы исключим первый случай (так как он неэффективен), что я должен использовать между &conan а также std::ref(conan)?

Это в первую очередь основано на мнении. По сути, они оба делают одно и то же, хотя первую версию написать короче.

Это как-то связано с синтаксисом, требуемым std::bind?

Вид. std::bind«s operator() также реализуется с помощью std::invoke поэтому у них очень общий интерфейс.


Все это говорит о том, что вы можете использовать лямбду, чтобы дать себе общий интерфейс.

thread t(&Warrior::attack, conan, 5);

может быть переписан как

thread t([&](){ return conan.attack(5); });

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

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]