简体   繁体   English

什么是在linux上c ++ / Qt中解雇和忘记线程的最简单方法?

[英]What is the simplest way to fire and forget a thread in c++ / Qt on linux?

I'm writing an app that embeds multiple libVlc instances running simultaneously using Qt. 我正在编写一个嵌入多个使用Qt同时运行的libVlc实例的应用程序。 There seems to be a bug in the vlc library where sometimes libvlc_media_player_stop deadlocks if called from the Qt's GUI thread. vlc库中似乎存在一个错误,如果从Qt的GUI线程调用,有时libvlc_media_player_stop会死锁。 On one of the videolan forums, the accepted solution was to call the stop function from another thread. 在其中一个videolan论坛上,接受的解决方案是从另一个线程调用stop函数。 I'm looking for the least involved and not-too-ugly method to call stop from a different thread. 我正在寻找最少参与且不太难看的方法来从不同的线程调用stop。 I looked at using QThreadPool which is meant exactly for this kind of situations but in my particular case, it doesn't make the solution pretty. 我看着使用QThreadPool,这意味着完全适用于这种情况,但在我的特殊情况下,它并没有使解决方案很好。

Here's a piece of my code: 这是我的一段代码:

VlcWidget.h VlcWidget.h

    class VlcWidget : public QWidget
    {
        Q_OBJECT

    private:

        // State
        bool _isPlaying;

        // The streaming source, title and quality data
        VideoData _videoData;
        VIDEO_QUALITY _quality;

        // LibVlc related members
        libvlc_instance_t *_vlcInstance;
        libvlc_media_player_t *_vlcMediaPlayer;
        libvlc_media_t *_vlcMedia;
        int _vlcTrackID;
    }

VlcWidget.c VlcWidget.c

    void VlcWidget::Play()
    {
        if(_videoData.Source() != "" && !_isPlaying)
        {
            // Create a new media descriptor
            _vlcMedia = libvlc_media_new_location(
                          _vlcInstance,
                          _videoData.Source().toStdString().c_str());

            // Tell the user about incorrect URL
            if(_vlcMedia == NULL)
            {
                QMessageBox::information(this,
                                         _videoData.Title(),
                                         "Unable to open source Url.\nPlease check the source and try again.");
                return;
            }

            libvlc_media_player_set_media(_vlcMediaPlayer, _vlcMedia);
            libvlc_media_release(_vlcMedia);
            libvlc_media_player_set_xwindow(_vlcMediaPlayer, parentWidget()->winId());
            libvlc_media_player_play(_vlcMediaPlayer);
            _vlcTrackID = libvlc_audio_get_track(_vlcMediaPlayer);
            _isPlaying = true;
        }
    }

    void VlcWidget::Stop()
    {
        if(_isPlaying)
        {
            libvlc_media_player_stop(_vlcMediaPlayer);
            _vlcTrackID = -1;
            _isPlaying = false;
        }
    }

My solution using QthreadPool looked like: 我使用QthreadPool的解决方案看起来像:

    class AsyncVlcPlay : public QRunnable
    {
    private:
         // State
        bool *_isPlaying;

        // LibVlc related members
        libvlc_instance_t *_vlcInstance;
        libvlc_media_player_t *_vlcMediaPlayer;
        libvlc_media_t *_vlcMedia;
        int *_vlcTrackID;

    public:
        virtual void run();
    }

And AsyncVlcPlay::run() does exactly what VlcWidget::Play() does with simple locking added to it. 并且AsyncVlcPlay :: run()与VlcWidget :: Play()完全相同,并添加了简单锁定。 And I'll also need a similar class for VlcWidget::Stop(). 而且我还需要一个类似VlcWidget :: Stop()的类。 I don't like this solution because I shouldn't really need 2 new classes for what I'm trying to achieve. 我不喜欢这个解决方案,因为我不应该为我想要实现的目标真正需要2个新类。 And worse than that is the fact that I'll have to pass VlcWidgets private members to another class' object. 更糟糕的是,我必须将VlcWidgets私有成员传递给另一个类的对象。 I'm pretty sure there's an extremely simple way that I'm not aware of and hope that one of you guys can help me out here. 我很确定这是一种非常简单的方式,我不知道,并希望你们中的一个人可以帮助我。 Thanks! 谢谢!

(In fact, I don't really need VlcWidget::Play() to be on another thread, but I'd like to keep Play and Stop symmetric) (事实上​​,我真的不需要VlcWidget :: Play()在另一个线程上,但我想保持Play和Stop对称)

I would tackle this problem with QThread. 我会用QThread来解决这个问题。 Its name is actually misleading as it's not actually a thread, but a thread controller and very easy to use. 它的名字实际上是误导性的,因为它实际上不是一个线程,而是一个线程控制器,非常容易使用。

Any class inherited from QObject can be moved to a thread and communication between threads can be done with the signal / slot mechanism. 从QObject继承的任何类都可以移动到一个线程,并且可以使用signal / slot机制完成线程之间的通信。 Therefore, you can do something like this: - 因此,您可以这样做: -

class VlcObject : public QObject
{
    Q_OBJECT

    public slots:
        void Run();

    private slots;
        void StopVlc();
};

This class can contain all the Vlc objects / instances. 该类可以包含所有Vlc对象/实例。 You then create a thread controller object and move the VlcObject instance to the new thread: - 然后创建一个线程控制器对象并将VlcObject实例移动到新线程: -

QThread* pThread = new QThread(this); // where this is a parent, running on the main thread
VlcObject* pVlcObj = new VlcObject;

pVlcObj->moveToThread(pThread);

// Note, this is Qt 5 connect style - Qt 4 connections also work
connect(pThread, &QThread::started, pVlcOj, &VlcObject::Run();

// Start the thread running
pThread->start();

Assuming QVlcWidget is a GUI class with a button called pStopVlc, you then call stop on the other thread's VlcObject by connecting the button to the VlcObject's StopVlc function: - 假设QVlcWidget是一个带有名为pStopVlc的按钮的GUI类,则可以通过将按钮连接到VlcObject的StopVlc函数来调用另一个线程的VlcObject上的stop: -

connect(pStopVlc, &QPushButton::released, pVlcObj, &VlcObject::StopVlc);

Alternatively, you could have StopVlc called when the thread quits and it is possible for the QThread to clean itself up when it stops: - 或者,您可以在线程退出时调用StopVlc,并且QThread可以在停止时自行清理: -

connect(pThread, &QThread::finished, pThread, &Qthread::deleteLater);

here is my (part of) code for coupling SWI-Prolog syntax highlighting to Qt 这是我的(部分)代码,用于将SWI-Prolog语法高亮显示耦合到Qt

// start highlighting, using SWI-Prolog syntax analyzer to collect structured data
//
void pqSource::startHighliter() {
    auto f = [](QString file, pqSyntaxData* psd) {
        SwiPrologEngine::in_thread _it;
        try {
            // ?? PlCall("consult", V(A(file)));
            qDebug() << "running syntax color on" << file << "thread" << CT;
            int rc = PlCall("syncol", PlTermv(A(file), PlTerm(psd)));
            qDebug() << "syncol" << rc;
        }
        catch(PlException e) {
            qDebug() << t2w(e);
        }
    };

    // collect structure asyncronously
    auto w = new QFutureWatcher<void>;
    connect(w, SIGNAL(finished()), this, SLOT(runHighliter()));

    // since could be a slow task, place a visual hint to what's going on...
    CenterWidgets((sd = new pqSyntaxData)->pgb = new QProgressBar(this));
    QTextCursor c = textCursor();
    c.movePosition(c.End);
    sd->pgb->setMaximum(c.position());
    connect(sd, SIGNAL(onProgress(int)), sd->pgb, SLOT(setValue(int)));
    sd->pgb->show();

    // run the Prolog snippet in background (hl pointer)
    w->setFuture(QtConcurrent::run(f, file, sd));
}

I think you could be interested in the use of lambda... 我想你可能对lambda的使用感兴趣...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM