QabtractItemModel线程安全

QAbstractItemModel thread safety

本文关键字:安全 线程 QabtractItemModel      更新时间:2023-10-16

我对QAbstractItemModel的实现正在听某些事件,并且在单独的线程中处理更新。更新的处理可能会导致模型中的布局和/或数据更改。数据本身的存储为boost::mutex-受保护,QAbstractItemModel的接口函数的每个调用(如果我正确理解,在GUI线程中执行),并且更新处理功能(在单独的线程中)锁定了Mutex。可以在锁定数据()/rowcount()/任何可能同时尝试获取的data()/rowcount()/dataChanged的信号时发出信号吗?

一块代码:

class MyItemModel : public QAbstractItemModel {
    Q_OBJECT
public:
    void processUpdate(const Update& update) {
        Mservice.post([this, update]() {
            boost::lock_guard<boost::mutex> lock (Mlock);
            bool willModifyLayout = checkWillModifyLayout(update)
            bool willModifyData = checkWillModifyData(update);
            if (willModifyLayout) {
                emit layoutAboutToBeChanged();
            }
                Mdata.processUpdate(update);
            if (willModifyLayout) {
                emit layoutChanged();
            }
            else if (willModifyData) {
                emit dataChanged();
            }            
        });
    }
    virtual QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE {
        boost::lock_guard<boost::mutex> lock (Mlock);
        if (index.isValid()) return Mdata.data(index, role);
    }
private:
    boost::mutex Mmutex;
    boost::asio::service Mservice;
    boost::asio::thread MserviceThread;
    DataStorage Mdata;
}

找到了我自己问题的答案:如果该模型属于其他QThread,则该模型的信号将使用QT :: QueuedConnection连接到视图,这很好。但是,如果(默认情况下)该模型属于GUI qthread(又称qcoreApplication :: instance() -> thread()),则将立即执行模型的插槽,从而导致data(columtCount(),columtCount(),等等,,没关系。

QAbstractItemModel不是线程安全。

这主要是因为跨线的信号被排队。

想象包含QList<int> list;的模型的以下方案(忽略QModelIndex):

background thread       GUI thread        +         signal queue, abbreviations for readability
[MODEL]                 [VIEW]                      ( )
beginInsertRows(0, 1);   ...                        ( rowsAboutToBeAdded(0, 1) = add(1) )
list << item();          (doing                     ( add(1) )
endInsertRows();        something                   ( add(1), rowsAdded(0, 1) = added(1) )
beginRemoveRows(0, 1);    else)                     ( add(1), added(1), rowsAboutToBeRemoved(0, 1) = rem(1) )
list.removeAt(0);        ...                        ( add(1), added(1), rem(1) )
endRemoveRows(0, 1);     ...                        ( add(1), added(1), rem(1), rowsRemoved(0, 1) = rmvd(1) )
                         rowsAboutToBeAdded(0, 1);  ( added(1), rem(1), rmvd(1) )
                         rowsAdded(0, 1);           ( rem(1), rmvd(1) )
                         possible crash!

原因:

在rowsadded()中,视图将迟早致电

model()->data(model()->index(0, 0));

此模型索引无效,因为该模型没有行。

在最好的情况下,它只是返回无效的QVariant()

在最坏的情况下(无防御检查),该模型试图访问list[0]