We know that in Qt the models are not thread-safe – they totally depend on your implementation.
So what we may do to bypass model between threads?
Guess that we have some models which implementation we can not adjust much or can not make thread-safe.
Danger is that if some model is used somewhere (pointer to model, saved QModelIndex, etc), then when we move model object to another thread, for some resource consuming processing, then some views or anything else may try to access it and you may get a crash.
But, there is one specific cool property that models have: they can be easily proxied.
Our idea is then that we make subclass of shared pointer (shared_ptr or QSharedPointer) and reimplement “->”. When some other object trys to access the smart pointer, it will get some proxy model (1:1 same data/structure/children/etc as source model).
Of course we have to use these smart pointers in all places where we would like to reference the model.
Moving the model to another thread (example for serialization or deserialization) is working in next way:
1. Call “detach()” on smart pointer – all other copies of smart pointers (we count them), will apply setSourceModel() to hidden proxy to use some stub single-cell model which is saying “Processing/Saving/Loading/…”
2. We bypass the real model to another thread “moveToThread()”, also send the smart_ptr with signal to holder in another thread
3. In another thread we do anything with model which is cpu/disc/etc consuming and may froze your UI if it were in GUI thread.
4. Send the smart pointer back to GUI thread, use “moveToThread()”
5. Call “attach” to attach our model to all smart pointer which got the model detached, then apply setSourceModel() to all hidden proxies.
In this way I implemented safe cpu/resource consuming operations on model datas while GUI was fully responsible!
3 Comments
Is it possible to get a code example of the global implementation ?
There’s nothing fundamentally thread-unsafe about a `QAbstractItemModel`. If you only access the model from views, then the model doesn’t have to be in the gui thread and it “just works”. Simply use `moveToThread` on the model and you’re done. If you want to call the model’s methods from your own code, you need to use `QMetaMethod::invoke`. That’s about it. You’re horribly overcomplicating things for no reason at all.
If you want, you can make a proxy model that uses `QMetaMethod::invoke` when the `sourceModel()->thread() != QThread::currentThread()`, otherwise it’d do direct calls to the source model. It also works great. This exposes a fully thread safe interface to a model, and can be accessed from any thread.