thread() 函数传参分析

thread() 函数传参分析

类对象(可调用对象的一中)作为thread函数的参数

代码

class Test
{
public:
    const int &ti;
    const int &tw;
    Test(int &i, int &w) : ti(i), tw(w) { cout << "调用构造函数" << endl; };
    Test(const Test &test) : ti(test.ti), tw(test.tw)
    {
        cout << "调用拷贝构造函数" << endl;
    }
    ~Test()
    {

        cout << "调用析构函数" << endl;
    }
    void operator()()
    {
        cout << "func 子线程开始" << endl;
        cout << "func 子线程处理任务............ " << endl;
        for (int i = 0; i < 5; i++) // 处理5s
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            cout << "Test::ti " << ti << "Test::tw  " << tw << endl;
        }

        cout << "func 子线程结束" << endl;
    }
};
int main(int argc, char **argv)
{
    cout << "主线程开始" << endl;
    int i = 5;
    int w = 2;
    Test test(i, w);
    thread t(test);
    t.detach();
    for (int i = 0; i < 3; i++) // 处理3s
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    cout << "主线程3s后结束" << endl;

打印结果

//     主线程开始
    // 调用构造函数
    // 调用拷贝构造函数
    // func 子线程开始
    // func 子线程处理任务............
    // Test::ti 5Test::tw  2
    // Test::ti 5Test::tw  2
    // 主线程3s后结束
    // 调用析构函数
    // Test::ti 340Test::tw  1170016112//    主线程开始

分析

  • 可以看到对象test 是被拷贝进线程函数的,所以这里的析构函数应该有两次,但 子线程 晚于于主线程结束,所以(拷贝对象的析构函数)没有输出出来*
  • 子线程在 主线程结束之后 i 和w 输出是 乱的 , 因为 子线程 晚于于主线程结束, 主线程的 i 和 w 在 结束后,被销毁,*
  • 子线程的函数引用了一段被释放的空间。 数据自然是乱的*

进一步处理代码

  • 现在将主线程的 代码修改为 执行8s ,使主线程 晚于子线程执行。
for (int i = 0; i < 8; i++) // 处理3s
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
          cout << "主线程8s后结束" << endl;
输出结果
主线程开始
    调用构造函数
    调用拷贝构造函数
    func 子线程开始
    func 子线程处理任务............
    Test::ti 5Test::tw  2
    Test::ti 5Test::tw  2
    Test::ti 5Test::tw  2
    Test::ti 5Test::tw  2
    Test::ti 5Test::tw  2
    func 子线程结束
    调用析构函数
    主线程8s后结束
    调用析构函数
分析
  • 可见调用了两次构造和析构; 且没有发生乱码现象

总结

  • 总结,在主线程,子线程分离的代码中,子线程函数不要 使用 引用和 指针 传递 变量

  • thread () 对传入的对象进行的拷贝。

函数对象和其参数 作为thread函数的参数

代码

 void myPrint(const int &li, const char *buf)
   //  void myPrint(const int &li, const string &buf)

{
    cout << "func 子线程开始" << endl;
    cout << "func 子线程处理任务............ " << endl;
    for (int i = 0; i < 5; i++) // 处理5s
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        cout << " myPrint::li  " << li << " myPrint::&li " << &li << "  myPrint::buf  " << buf << " myPrint::&str  " << (int *)buf << endl;

    }

    cout << "func 子线程结束" << endl;
}

int main(int argc, char **argv)
{
    cout << "主线程开始" << endl;
    int ri = 100;
    int &li = ri;
    char *buf = "hello world";
    cout << " myPrint::li  " << li << " main::&li  " << &li << endl;
    cout << " myPrint::buf " << buf << " main::&buf  " << (int *)buf << endl;
    thread t(myPrint, li, buf);
    t.detach();
    for (int i = 0; i < 3; i++) // 处理3s
    {

        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    cout << "主线程3s后结束" << endl;
}

执行结果

// 主线程开始
 myPrint::li  100 main::&li  0x228cdffa0c
 myPrint::buf hello world main::&buf  0x7ff6315b60cc
func 子线程开始
func 子线程处理任务............
 myPrint::li  100 myPrint::&li 0x13497b76170  myPrint::buf  hello world myPrint::&str  0x7ff6315b60cc
 myPrint::li  100 myPrint::&li 0x13497b76170  myPrint::buf  hello world myPrint::&str  0x7ff6315b60cc
主线程3s后结束
 myPrint::li  100 myPrint::&li 0x13497b76170  myPrint::buf  hello world myPrint::&str  0x7ff6315b60cc

分析

  • 可以看到 在子线程和 主线程 中 li 的 地址是不同的,而 buf 的地址是相同的, 这意味这在 主线程结束后, li 结果是正确的(因为不会引用到销毁的地址空间),而 buf 的值会出错(因为子线程晚于主线程结束,却引用着已经销毁的变量buf)
  • 可见 std::thread 的入口函数中传递简单类型数据,如 int 等,本质上是值传递,可以放心的 detach(不加std::ref的情况下)
  • std::thread 的入口函数中传递指针时,是引用传递,引用的是和main 线程中指针变量 同一块内存

演示出现的问题

  • 解决1 将线程函数的 const char *buf 参数 换为 const string & buf ,这样就会生成临时对象的转换, 就不会引用到主线程的buf
  • 但这种临时对象的转换是发生在子线程的,一旦主线程结束,char* buf被回收了, 就不能被转换为 string &buf了。
  • 演示一下 临时对象的转换发生在子线程中(转步到 thread05.cpp)
隐式类型转换出现在子线程中
// TAG 演示临时对象的转换发生在子线程中
class A
{
public:
    int m_i;
    A(int i) : m_i(i)
    {

        this_thread::sleep_for(std::chrono::seconds(2)); // 让子线程后于主线程结束。 如果转换发生在 主线程,那主线程一定会打印这句话,才会结束掉

        cout << "A::A(int i) : 构造函数被执行 " << endl;
    }
    A(const A &other) : m_i(other.m_i) { cout << "A::A(const A& other) 拷贝构造函数被执行" << endl; }
    ~A() { cout << "A::~A() 析构函数被执行" << endl; }
};
void func(const int i, const A &a)
{
    cout << &a << endl;
    return;
}
int main()
{
    int i = 25;
    int a = 100;
    cout << "主线程开始" << endl;
    cout << "子线程开始:" << endl;
    thread t(func, i, a); // 希望将整型的 a 转换为  A 类型的a 对象

    t.detach();
    cout << "主程序结束" << endl;
}
// 输出
// 主线程开始
// 子线程开始:
// 主程序结束
// 可见主程序结束前并未打印出A的构造函数,说明 主程序并未发生临时对象的转换。

解决方法

  • 可以在thread 函数数中, 使用 string() 强制转换,让临时对象的转换发生在 主线程中, 就能避免在子线程中转换的时候,buf被回收了。
// TAG 演示临时对象的转换发生在子线程中
class A
{
public:
    int m_i;
    A(int i) : m_i(i)
    {

        this_thread::sleep_for(std::chrono::seconds(2)); // 让子线程后于主线程结束。 如果转换发生在 主线程,那主线程一定会打印这句话,才会结束掉

        cout << "A::A(int i) : 构造函数被执行 " << endl;
    }
    A(const A &other) : m_i(other.m_i) { cout << "A::A(const A& other) 拷贝构造函数被执行" << endl; }
    ~A() { cout << "A::~A() 析构函数被执行" << endl; }
};
void func(const int i, const A &a)
{
    cout << &a << endl;
    return;
}
int main()
{
    int i = 25;
    int a = 100;
    cout << "主线程开始" << endl;
    cout << "子线程开始:" << endl;
    thread t(func, i, A(a)); // 希望将整型的 a 转换为  A 类型的a 对象,
                             // todo  使用 强制类型转换后, 就能使得类型转换在主线程中进行

    t.detach();
    cout << "主程序结束" << endl;
}
// 输出结果
// 主线程开始
// 子线程开始:
// A::A(int i) : 构造函数被执行
// A::A(const A& other) 拷贝构造函数被执行
// A::~A() 析构函数被执行
// 主程序结束
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
在PyQt6中,QThread是一个用于在后台执行耗时任务的类。它可以与其他类一起使用,以实现多线程编程。关于QThread函数传参的介绍如下: 1. 通过构造函数传参:可以在创建QThread对象时,通过构造函数传递参数。例如: ```python class MyThread(QThread): def __init__(self, param): super().__init__() self.param = param def run(self): # 在这里执行耗时任务,可以使用self.param访问传递的参数 pass # 创建线程对象并传递参数 thread = MyThread("参数值") ``` 2. 通过成员函数传参:可以在创建QThread对象后,通过成员函数设置参数。例如: ```python class MyThread(QThread): def __init__(self): super().__init__() self.param = None def set_param(self, param): self.param = param def run(self): # 在这里执行耗时任务,可以使用self.param访问传递的参数 pass # 创建线程对象并设置参数 thread = MyThread() thread.set_param("参数值") ``` 3. 通过信号槽传参:可以使用信号槽机制,在主线程和子线程之间传递参数。例如: ```python class MyThread(QThread): param_changed = pyqtSignal(str) def __init__(self): super().__init__() self.param = None def set_param(self, param): self.param = param self.param_changed.emit(param) def run(self): # 在这里执行耗时任务,可以使用self.param访问传递的参数 pass # 创建线程对象并设置参数 thread = MyThread() thread.param_changed.connect(thread.set_param) thread.set_param("参数值") ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丁金金

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值