C++ Qthread多线程意外结果

C++ Qthread多线程意外结果,c++,multithreading,qt,C++,Multithreading,Qt,我想用我的代码实现以下几点: 两个线程,同时向控制台打印“foo”和“bar”100次。秩序不重要 创建线程的对象需要在线程完成时获取信号。在现实生活中,这个信号将携带线程的结果,创建者需要等待这个结果 这是我为本例编写的代码: main.cpp 首先,我们创建这个对象和一个QThread对象。在堆或堆栈中创建QThread对象时要小心。这将导致两种不同的结束线程的方法 QThread stackThread; Object workerStack; QThread* heapThread =

我想用我的代码实现以下几点:

  • 两个线程,同时向控制台打印“foo”和“bar”100次。秩序不重要

  • 创建线程的对象需要在线程完成时获取信号。在现实生活中,这个信号将携带线程的结果,创建者需要等待这个结果

  • 这是我为本例编写的代码:

    main.cpp 首先,我们创建这个对象和一个
    QThread
    对象。在堆或堆栈中创建
    QThread
    对象时要小心。这将导致两种不同的结束线程的方法

    QThread stackThread;
    Object workerStack;
    QThread* heapThread = new Qthread;
    Object workerHeap;
    
    connect(&workerStack, &Object::finish, &stackThread, &QThread::quit);
    connect(&workerHeap, &Object::finish, heapThread, &QThread::quit);
    
    创建线程和辅助对象后,只需将辅助对象移动到线程。这里没有魔法

    workerStack.moveToThread(&stackThread);
    workerHeap.moveToThread(heapThread);
    
    然后连接一个信号,以便在调用
    QThread::start
    时,对象开始工作:

    connect(&stackThread, &QThread::started, &workerStack, &Object::job1);
    connect(heapThread, &QThread::started, &workerHeap, &Object::job2);
    
    您可以使用
    finish()
    信号停止线程

    QThread stackThread;
    Object workerStack;
    QThread* heapThread = new Qthread;
    Object workerHeap;
    
    connect(&workerStack, &Object::finish, &stackThread, &QThread::quit);
    connect(&workerHeap, &Object::finish, heapThread, &QThread::quit);
    
    您可能还需要一个信号槽,以便在堆中创建的线程可以在完成时删除自身。在堆栈中创建的线程不需要这样做

    connect(&workerHeap, &Object::finish, heapThread, &QThread::deleteLater);
    
    如果您的
    finish()

    connect(&workerStack, &Object::finish, this, &THISSCOPE::catchFinish);
    
    如果需要等待线程结果以继续程序,可以使用
    QEventLoop
    。请不要使用不应该执行此任务的
    QThread::wait
    函数。
    wait
    函数只是阻止当前事件循环,这样您就永远无法捕捉到
    finish
    信号

    虽然使用
    QEventLoop
    可以让您接收
    finish
    信号,但这不是一个好的设计。有很多方法可以避免这种情况,但我在此不作详细说明。我只是用
    QEventLoop
    来演示当前示波器如何接收信号。代码如下:

    QEventLoop eventloop;
    connect(&workerStack, &Object::finish, &eventloop, &QEventLoop::quit);
    
    现在,我们可以在创建所有插槽后启动线程:

    threadStack.start();
    threadHeap.start();
    
    如果需要等待结果,请启动eventloop:

    eventloop.exec();
    
    当线程完成时,在堆中创建的线程应该发出
    finish
    信号并删除自身(因为您连接到了它的
    deleteLater
    插槽)。但是,在堆栈上创建的线程有点棘手。如果主代码超出了此处的范围,则可能会出现错误“QThread在运行时被销毁”。这是因为主代码比
    QThread::quit
    快一点。因此,您需要在这里放置一个
    QThread::wait
    wait
    函数的真正用途是在作业完成后等待线程退出不是等待作业完成。最后,我们加上:

    threadStack.wait();
    

    您描述的不同行为和错误很难调试。但是,当线程的创建、销毁、开始和结束得到正确实现时,您可能会看到错误消失,行为更加一致

    一些提示:

    • 您不需要初始化QEventLoop实例。默认的QThread实现已经有一个eventLoop(读和)
    • 启动线程的正确方法是调用。如果连接到Controller::startWrite()并删除
      emit startWrite(),程序将执行相同的任务
    从您的示例中,您有(至少)2个解决方案来改进实施:

  • 按原样使用textWriter类。在这种情况下,您可以创建一个QThread和一个textWriter,使用moveToThread将textWriter实例移动到QThread,将QThread::start连接到textWriter::write,并将textWriter::finish连接到QThread::quit,最后使用QThread::start()启动线程
  • 另一个解决方案是修改textWriter。您可以将textWriter转换为QRunnable的子级,并将textWriter::write重命名为。然后使用QThreadPool启动执行
  • 在任何情况下,我建议您阅读一篇总结,这可能会帮助您找到符合您具体情况的解决方案

    祝你好运;)

    错误太多了

    除了安特万先前的回答之外,没有更多的评论

    • 为堆栈上分配的对象调用
      delete
      是个坏主意。对象
      threadA
      threadB
      位于
      控制器的堆栈上。当构造函数完成时,它们会自动删除,并且由于
      deleteLater()
      的原因,它们会被第二次删除


    • 在运行
      eventloopA
      eventloopB
      时,主事件循环被阻塞。因此,在两个线程都未完成之前,
      控制器
      对象无法接收任何信号。不需要阻止
      控制器
      构造函数。我猜阻止它的想法来自于线程对象被破坏的问题。但是,可以使用
      new
      创建线程对象以避免该问题

    • 一般来说,通过线程的
      finished
      信号删除线程是不好的。通常,应用程序可能在调用
      QThread::quit()
      和发出
      QThread::finished()
      信号之前关闭


    请参见
    QThread
    中的
    controller
    示例<代码>QThread
    可以在其他偶数循环中删除(不是它自己的循环)。但是,即使当主偶数循环(主线程)捕捉到线程
    QThread::finished()
    信号时,
    QThread
    仍可能正忙。为了确保线程在删除之前真正完成,有函数
    QThread::wait()

    我初始化QEventLoop,因为我需要等待来自textWriter的信号。是否有其他方法可以暂停控制器并等待信号?我发现
    连接(&threadB,&QThread::finished,&threadB,&QThread::deleteLater)导致错误的不稳定性:双重自由或腐败使用您的建议
    
    QEventLoop eventloop;
    connect(&workerStack, &Object::finish, &eventloop, &QEventLoop::quit);
    
    threadStack.start();
    threadHeap.start();
    
    eventloop.exec();
    
    threadStack.wait();