QSerialPort 멀티스레드 어플리케이션에 대한 개선 사항

4324 단어 Qt
클래스 Unix시스템의 장치 인터페이스는 select 기반 이벤트 드라이브를 사용하기 때문에 장치의 대상은 반드시 하나의 라인에 존재해야 한다. select 이벤트는 장치의 크로스 라인에서 직접 전송할 수 없고 듀플렉스 장치의 크로스 라인 조작도 직접 실현할 수 없다.Qt는 크로스플랫폼 개발 라이브러리로서 클래스 유닉스 시스템의 이벤트 구동을 호환하기 위해 비슷한 제한으로 설계되었다.
듀플렉스 인터페이스의 QSerialPort 대상에게 데이터 전송 압력이 시간에 비해 직접적으로 메인 라인에서readyRead 신호를 위한 슬롯 함수를 통해 데이터 수신을 편리하게 실현할 수 있고 메인 라인에서 UI와 상호작용하는 데이터 전송 작업도 잘 수행할 수 있다.그러나 어느 방향의 데이터 전송 압력이 비교적 크면 주 스레드가 UI의 이벤트 순환에 응답해야 하기 때문에 이 방향의 데이터 전송이 지연될 수 있기 때문에 주 스레드의 UI 이벤트 순환을 피하기 위해 하위 스레드로 옮겨야 한다. 즉, 듀플렉스 장치의 크로스 스레드 조작이 필요하다는 것이다.
그렇다면 Qt를 바탕으로 이중 작업 설비의 크로스 라인 조작을 어떻게 설계합니까?직렬 인터페이스를 예로 들면 Qt의 막힘식 크로스 스레드 신호 슬롯 연결 메커니즘을 사용하여 발동 스레드에서 신호를 발사하여 매개 변수를 전달한 후 슬롯 함수가 실행되기를 기다릴 수 있다.직렬의 신호가 연결된 슬롯 함수에서 대응하는 조작 함수를 호출하고 함수 결과를 클래스 구성원 변수에 저장하여 중회전시킨 다음에 되돌려준다.작업 발동 루틴 막힘이 끝난 후, 클래스 구성원 변수를 읽어서 작업 결과를 가져오고 출력합니다.직렬 인터페이스의 write 조작의 구체적인 실현 방법을 예로 들면 새로운 방법은threadSafeWrite이고 QSerialPort에서 계승된 사용자 정의 클래스는QThreadSafeSerialPort이다.
class QThreadSafeSerialPort : public QSerialPort
{
    Q_OBJECT
    Q_DISABLE_COPY(QThreadSafeSerialPort)
    QMutex m_mutex;
    qint64 m_resInt64;
Q_SIGNALS:
    void sigWrite(char const* data);
public:
    QThreadSafeSerialPort(QObject* parent = Q_NULLPTR) : QSerialPort(parent) {
        connect(this, &sigWrite, this, [&](char const* data){m_resInt64 = write(data);}, Qt::BlockingQueuedConnection);
    }

    qint64 threadSafeWrite(char const* data) {
        if (QThread::currentThread() == thread()) {
            return write(data);
        } else {
            QMutexLocker locker(&m_mutex);
            emit sigWrite(data);
            return m_resInt64;
        }
    }

신호 슬롯 메커니즘을 사용했기 때문에 QThreadSafeSerialPort 대상이 있는 라인은 이벤트 순환을 사용해야 한다. 그렇지 않으면 크로스 라인의threadSafeWrite 호출이 정확하게 실행되지 않는다.이벤트 순환을 실행하기 위해서는 QThread 클래스의run 함수에서 exec()를 실행해야 합니다.exec () 는 이벤트 순환이 끝날 때까지 되돌아오지 않습니다. 이것은 런 함수에서 막힌 데이터 수신이나 발송을 실행하는 방안을 실현할 수 없게 하기 때문에 슬롯 함수를 실현하고ready Read/bytes Written 신호를 연결해서 데이터 수신/발송을 실현해야 합니다.데이터가 수신/발송되는 슬롯 함수를 서브스레드에서 실행하기 위해 슬롯 함수에 첨부된 QObject 계승 클래스의 대상은 서브스레드에서 생존해야 합니다.예제 코드는 다음과 같습니다.
class ThreadSlotHelper : public QObject
{
    Q_OBJECT
public:
    ThreadSlotHelper(void) : QObject(Q_NULLPTR) {}
public Q_SLOTS:
    void onReadyRead(void) {
        // do read work...
    }
};
class ExampleThread : public QThread
{
    Q_OBJECT
    ThreadSlotHelper m_helperObj;
    QThreadSafeSerialPort m_port;
protected:
    virtual void run(void) {
        m_port.open(QIODevice::ReadWrite);
        exec();
        m_port.close();
    }
public:
    ExampleThread(QObject* parent = 0) : QThread(parent){
        m_helperObj.moveToThread(this);
        m_port.moveToThread(this);
        connect(&m_port, SIGNAL(readyRead()), &m_helperObj, SLOT(onReadyRead()));
    }
};

성능 분석: 하위 스레드에서 이벤트 순환을 사용했지만 하위 스레드에 UI가 없기 때문에 UI 이벤트를 실행하지 않아도 되고 하위 스레드의 모든 이벤트 순환은 직렬 통신 이벤트에 사용할 수 있기 때문에 하위 스레드의 성능 손실이 뚜렷하지 않다.크로스 스레드의threadSafeWrite 호출은 막힌 신호 연결 방식으로 신호와 슬롯 함수를 연결하기 때문에 크로스 스레드가 이벤트 순환의 집행을 기다려야 하기 때문에 그 집행 효율이 비교적 낮을 것이다. 이것은 크로스 스레드 신호 슬롯 메커니즘을 사용하는 데 반드시 지불해야 하는 대가이다.threadSafeWrite의 실현 방안이 성능 요구를 충족시키지 못하면 데이터 읽기와 쓰기 방안을 다시 설계해야 합니다.

좋은 웹페이지 즐겨찾기