Programación concurrente en C++ (2): paso de parámetros a funciones de subprocesos

Pasar parámetros a la función de hilo

Principalmente dividido en 3 situaciones.

  1. función ordinaria
  2. función miembro de clase
  3. El parámetro es un puntero inteligente.

Se debe prestar especial atención al hecho de que el subproceso tiene espacio de almacenamiento interno , y los parámetros se copiarán como una variable temporal de forma predeterminada, y luego la variable temporal se pasará como un valor r a la función u objeto invocable en el nuevo subproceso.

Paso de parámetros de función ordinaria

#include <iostream>
#include <thread>
#include <string.h>
using namespace std;

// void myPrint(string& t_)
// void myPrint(const string &t_)
void myPrint(string&& t_)
{
    
    
		t_ = "Ouch!";
    cout << "t_ location:" << &t_ << endl;
    cout << "myPrint:" << t_ << endl;
}

int main()
{
    
    
    char buff[]="Watch out!";
    string text = "Hello C++!";
    cout << "text location:" << &text << endl;
    cout << "buff location:" << &buff << endl;
		cout << "更改前text:" << text << endl;

    // thread t(myPrint, ref(text));
    thread t(myPrint, text);
    t.join();
    // t.detach();

		cout << "更改后text:" << text << endl;	
	
    cout << "main end!" << endl;

    return 0;
}

punto importante:

1. Debido a que se pasa a la función como un valor r, las siguientes referencias ordinarias no son factibles y la compilación no pasará

void myPrint(string& t_)

Por lo tanto, solo se pueden usar otros dos métodos de referencia.

2. El parámetro se ha copiado una vez antes de pasar, por lo que la referencia toma la dirección de la variable temporal, y el cambio en la función no afectará el parámetro real. Ejecute el programa anterior y la salida

text location:0x7ffd5d2f84d0
buff location:0x7ffd5d2f84fd
更改前text:Hello C++!
t_ location:0x55dbf3e3e288
myPrint:Ouch!
更改后text:Hello C++!
main end!

Se comprueba que la dirección es diferente y el contenido del texto no ha cambiado, por lo que puede entenderse como una referencia falsa

Si es necesario pasar parámetros por referencia, deben envolverse con la función std::ref(), que es similar a la función std::bind(), de la siguiente manera:

thread t(myPrint, ref(text));

Salida después de ejecutar

text location:0x7ffcaaa3e6f0
buff location:0x7ffcaaa3e71d
更改前text:Hello C++!
t_ location:0x7ffcaaa3e6f0
myPrint:Ouch!
更改后text:Ouch!
main end!

Tenga en cuenta que la declaración de la función en este punto es

void myPrint(string& t_)

En este momento, es una referencia lvalue que entendemos normalmente

Función miembro de clase que pasa parámetros

Se puede dividir en dos casos, en función de si la función miembro es una función miembro estática

1. Funciones miembro estáticas

#include <iostream>
#include <thread>
#include <string.h>
using namespace std;

class A
{
    
    
public:
    A(int a):m_a(a)
    {
    
    
        cout << "A 构造函数 。。。" << endl;
    }
    A(const A& a):m_a(a.m_a)
    {
    
    
        cout << "A 拷贝构造函数 。。。" << endl;
    }

    void operator()(){
    
    }

    static void printtext(string& t_)
    // void printtext(const string& t_)
    {
    
    
        t_ = "Ouch!";
        cout << "t_ location:" << &t_ << endl;
        cout << "t_:" << t_ << endl;
    }

    ~A(){
    
    cout << "A 析构函数 。。。" << endl;}
public:
    int m_a;
};

int main()
{
    
    
    char buff[]="Watch out!";
    string text = "Hello C++!";
    cout << "text location:" << &text << endl;
    cout << "buff location:" << &buff << endl;
    cout << "更改前text:" << text << endl;

    A A1(5);
    // thread t(A1);
    // thread t(&A::printtext,&A1,text);
    thread t(&A::printtext,ref(text));
    t.join();
    // t.detach();

    cout << "更改后text:" << text << endl;
    
    cout << "main end!" << endl;

    return 0;
}

Debido a que la función miembro estática se encuentra en el área global de la memoria, pertenece a la clase y no al objeto de la clase, por lo que basta con indicar que la función está bajo el alcance de la clase.

thread t(&A::printtext,ref(text));

En este momento, tomar el carácter de dirección no tiene ningún efecto, y lo siguiente también es factible

thread t(A::printtext,ref(text));

La salida es la siguiente:

text location:0x7ffdda801e90
buff location:0x7ffdda801ebd
更改前text:Hello C++!
t_ location:0x7ffdda801e90
t_:Ouch!
更改后text:Ouch!
main end!

2. Funciones miembro no estáticas

En este momento, si desea establecer una función miembro de una determinada clase como una función de subproceso, debe pasar un puntero de función que apunte a la función miembro y también dar un puntero de objeto, de la siguiente manera:

thread t(&A::printtext,&A1,text);

Hay un lugar muy digno de mención, hay dos símbolos de dirección, si se elimina el primero, se informará un error

/home/prejudice/Cplus_learning/src/thread_02.cpp:54:17: error: invalid use of non-static member function ‘void A::printtext(std::__cxx11::string&&)’
     thread t(A::printtext,&A1,text);

Por lo tanto, el primer símbolo de dirección debe reservarse, luego mire el segundo

Si se elimina el & del segundo parámetro, la salida en ejecución es la siguiente:

text location:0x7ffd84776370
buff location:0x7ffd8477639d
更改前text:Hello C++!
A 构造函数 。。。
A 拷贝构造函数 。。。
A 拷贝构造函数 。。。
A 析构函数 。。。
t_ location:0x559be66f7288
t_:Ouch!
A 析构函数 。。。
更改后text:Hello C++!
main end!
A 析构函数 。。。

¿Por qué hay tres construcciones y tres destrucciones?, así lo entiendo

text location:0x7ffd84776370
buff location:0x7ffd8477639d
更改前text:Hello C++!
A 构造函数 。。。          //main中A1的构造
A 拷贝构造函数 。。。      //拷贝构造一个临时对象
A 拷贝构造函数 。。。      //将该临时对象拷贝到printtext函数中,作为该函数内的局部变量
A 析构函数 。。。          //临时对象使用完毕立即释放
t_ location:0x559be66f7288
t_:Ouch!
A 析构函数 。。。          //printtext函数运行结束释放栈区上的数据
更改后text:Hello C++!
main end!
A 析构函数 。。。          //mian结束调用A1的析构

Si mantiene la salida & ejecutar del segundo parámetro de la siguiente manera

text location:0x7ffe6078fd40
buff location:0x7ffe6078fd6d
更改前text:Hello C++!
A 构造函数 。。。
t_ location:0x55af5cc36288
t_:Ouch!
更改后text:Hello C++!
main end!
A 析构函数 。。。

Se puede ver que solo se construye una vez en la función principal, eliminando la necesidad de dos copias, lo que mejora la eficiencia operativa.

Por lo tanto, se recomienda pasar punteros de función y punteros de objeto al mismo tiempo

Aquí también preste atención a las referencias verdaderas y falsas de los parámetros.

El parámetro es un puntero inteligente.

Hay tres tipos de punteros inteligentes:

  1. Puntero exclusivo - unique_ptr
  2. Puntero compartido - shared_ptr
  3. Puntero débil - débil_ptr

Aquí solo hablamos de punteros exclusivos y punteros compartidos como parámetros, porque los punteros débiles rara vez se ven en los códigos que he leído hasta ahora.

Puede completar su conocimiento primero y luego leer el siguiente contenido

http://c.biancheng.net/view/1478.html

unique_ptr como parámetro

#include <iostream>
#include <thread>
#include <string.h>
#include <memory>
using namespace std;

// void myPrint(string& t_)
// void myPrint(const string &t_)
// void myPrint(string&& t_)
void myPrint(unique_ptr<string> t_)
//void myPrint(shared_ptr<string> t_)
{
    
    
    *t_ = "Ouch!";
    cout << "t_ location:" << &t_ << endl;
    cout << "myPrint:" << *t_ << endl;
}

int main()
{
    
    
    // string text = "Hello C++!";
    // unique_ptr<string> text(new string);
    unique_ptr<string> text = make_unique<string>("Hello C++!");
    // shared_ptr<string> text = make_shared<string>("Hello C++!");
    cout << "text location:" << &text << endl;
    cout << "更改前text:" << *text << endl;

    thread t(myPrint,move(text));
    t.join();
    // t.detach();

    // cout << "更改后text:" << *text << endl;
    
    cout << "main end!" << endl;

    return 0;
}

Ejecutar salida:

text location:0x7ffd55a3c1e8
更改前text:Hello C++!
t_ location:0x7f8bb48e8dc0
myPrint:Ouch!
main end!

punto importante:

1. Use la función de movimiento para transferir la propiedad del objeto dinámico al hilo. Después de la transferencia, el texto se convierte en un puntero NULL, por lo que debe comentar

// cout << "更改后text:" << *text << endl;

De lo contrario, se solicitará un error de segmento después de la ejecución y el núcleo se habrá volcado

2. make_unique está en el estándar C ++ 14, por lo que debe cambiar CMakeLists.txt

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -pthread")

De lo contrario, se informará un error al compilar

error: ‘make_unique’ was not declared in this scope
     unique_ptr<string> text = make_unique<string>();

shared_ptr es un parámetro

#include <iostream>
#include <thread>
#include <string.h>
#include <memory>
using namespace std;

// void myPrint(string& t_)
// void myPrint(const string &t_)
// void myPrint(string&& t_)
// void myPrint(unique_ptr<string> t_)
void myPrint(shared_ptr<string> t_)
{
    
    
    *t_ = "Ouch!";
    cout << "t_ location:" << &t_ << endl;
    cout << "myPrint:" << *t_ << endl;
}

int main()
{
    
    
    // string text = "Hello C++!";
    // unique_ptr<string> text(new string);
    // unique_ptr<string> text = make_unique<string>("Hello C++!");
    shared_ptr<string> text = make_shared<string>("Hello C++!");
    cout << "text location:" << &text << endl;
    cout << "更改前text:" << *text << endl;

    thread t(myPrint, text);
    // thread t(myPrint,move(text));
    t.join();
    // t.detach();

    cout << "更改后text:" << *text << endl;
    
    cout << "main end!" << endl;

    return 0;
}

Debido a que todos los punteros shared_ptr apuntan a la misma dirección de bloque, el texto todavía se puede imprimir en main

text location:0x7fffc22c9410
更改前text:Hello C++!
t_ location:0x7fd464a9bdc0
myPrint:Ouch!
更改后text:Ouch!
main end!

También use la función miembro de la clase para probar

#include <iostream>
#include <thread>
#include <string.h>
#include <memory>
using namespace std;

class A
{
    
    
public:
    A(int a) :m_a(a)
    {
    
    
        cout << "A 构造函数 。。。" << endl;
    }
    A(const A& a) :m_a(a.m_a)
    {
    
    
        cout << "A 拷贝构造函数 。。。" << endl;
    }

    void operator()() {
    
    }

    //void printtext(string& t_)
    // void printtext(const string& t_)
    void printtext(shared_ptr<string> t_)
    {
    
    
        *t_ = "Ouch!";
        cout << "t_ location:" << &t_ << endl;
        cout << "t_:" << *t_ << endl;
    }

    ~A() {
    
     cout << "A 析构函数 。。。" << endl; }
public:
    int m_a;
};

int main()
{
    
    
    // string text = "Hello C++!";
    // unique_ptr<string> text(new string);
    // unique_ptr<string> text = make_unique<string>("Hello C++!");
    shared_ptr<string> text = make_shared<string>("Hello C++!");
    cout << "text location:" << &text << endl;
    cout << "更改前text:" << *text << endl;

    A A1(5);
    thread t(&A::printtext, &A1, text);
    t.join();

    cout << "更改后text:" << *text << endl;

    cout << "main end!" << endl;

    return 0;
}

imprimir:

text location:0098FC38
更改前text:Hello C++!
A 构造函数 。。。
t_ location:00AFFCC4
t_:Ouch!
更改后text:Ouch!
main end!
A 析构函数 。。。

Noté que la ubicación t_ y la ubicación del texto son diferentes, ya sea una función ordinaria o una función miembro. Al final, el texto se puede modificar con éxito. Tengo que suspirar el poder de C++

En cuanto a la razón, puede implicar una lógica de nivel relativamente bajo. Si está interesado, puede aprender por sí mismo.

Supongo que te gusta

Origin blog.csdn.net/Solititude/article/details/131738709
Recomendado
Clasificación