Thread-safe subclass of QStandardItemModel?
-
I subclassed QStandardItemModel to create a tree structure that holds various configuration data with some particular features.
Everything was going well until I started to modify the data from threads other than the UI. Hell, they're not even Qt threads, they're MFC worker threads.So, I realized that QStandardItemModel is NOT thread-safe... But I haven't found a way to make it so. Even if I add locks in all my subclass' methods, every method that is not overridden still does not lock properly, since they are processed by QAbstractItemModel, or QStandardItemModel.
The "only article":http://stackoverflow.com/questions/4416706/modifying-qstandarditemmodel-from-non-ui-qthread I found regarding solutions to this mentions signals and slots, but I'm not quite sure what he means... Do I need to make ALL my public methods slots, create a signal in the caller class, connect it with a BlockingQueuedConnection, and emit a signal instead of calling the method? This seems awfully painful for the users of my model subclass... Also, I'm not sure how it would help for methods that are part of the QStandardItemModel or QAbstractItemModel classes, which I can't change to slots.
Also, I have a subclass of QAbstractProxyModel that interacts with my model through the methods given by QAbstractItemModel. I'm guessing my proxy model should also 'lock' the model in some way...
I'm lost! Any help appreciated... Thanks.
-
It sounds like you're using MFC migration framework(I also use it in my work). Because MFC worker thread is not Qt thread, you will not use signal in that worker thread, AFAIK.
Maybe you will have to use QObject::metaObject() to get QMetaObject and call its invokeMethod() to call the final mem func.
BTW, unlike MFC and other win32 gui, Qt can use UI object in only one thread(the main thread). Make gui operation atomically would not help in that case(at the end of any operation you should call syn method like QWidget::update() ...). In Qt way, you have to separate your logic and gui with signal and slots. -
You could also go another way:
Keep your data in a class (CMyData) which is thread safe and create a custom model ontop of that that is triggered by the thread safe class. Ok, you loos the QStandardItemModel features, but you get thread safety.
-
I'm not quite sure to understand this right... Even if my entire application was Qt, how would this work? I understand that interacting with a model through a view goes through the UI thread... but what if another thread wants to manipulate the model? It will call methods of the model directly in its own thread, no?
[quote author="joonhwan" date="1336006658"]It sounds like you're using MFC migration framework(I also use it in my work). Because MFC worker thread is not Qt thread, you will not use signal in that worker thread, AFAIK.
Maybe you will have to use QObject::metaObject() to get QMetaObject and call its invokeMethod() to call the final mem func.
BTW, unlike MFC and other win32 gui, Qt can use UI object in only one thread(the main thread). Make gui operation atomically would not help in that case(at the end of any operation you should call syn method like QWidget::update() ...). In Qt way, you have to separate your logic and gui with signal and slots.[/quote]
I think one of my issues is that some of the operations on my model involve multiple deletes and inserts, and I might be emitting a lot of 'dataChanged' signals... Is there some way to tell the model to stop updating connected views during a long operation, and emit a single 'dataChanged' at the end?
Do I need to inherit from QAbstractItemModel instead to do that? As Gerolf said, I'd get much more liberty and control over thread safety, but it involves some pretty heavy refactoring :( -
My approach would be: separate model and data.
Create the model as interface to the the data and then the data can be thread safe.
Yes this implies refactoring, but it enables threading for this thing.
We needed similar things in our projects.What we did was:
worker thread is collecting the data. It keeps a copy and checks the diff.
worker thread send the diff via a signal to the model
the model calculates the changes and emits data change / insert/remove etc in the ui thread.
-
Before you go further to implement 'diff' thing(and/or Gerolf's advice), just let your worker thread invoke model's member asynchronously using Qt event system.
Unlike win32's event pumping system, it doesn't lose any event even if lots of events are queued(this is my own experience though only in Windows xp).
Not every time model data is updated, view is updated forcefully. All GUI updates will handled in compact'ed way in Qt like any other UI toolkit/API.
After finishing this simpler approach, check performance and then go further to introduce a good kinda proxy class like the one Gerolf advised. I'd do things like this. Using MFC and Qt simulateneously, it is quite easy to make any mistake when handling intra-thread implementation(MFC has its own thread context data and quite often assuming that other thread has it too). I would keep things simple as much as possible.
I'd be happy to hear another your updates on this implementation. Cheer up.
[quote author="samapico" date="1336055661"]I'm not quite sure to understand this right... Even if my entire application was Qt, how would this work? I understand that interacting with a model through a view goes through the UI thread... but what if another thread wants to manipulate the model? It will call methods of the model directly in its own thread, no?
[quote author="joonhwan" date="1336006658"]It sounds like you're using MFC migration framework(I also use it in my work). Because MFC worker thread is not Qt thread, you will not use signal in that worker thread, AFAIK.
Maybe you will have to use QObject::metaObject() to get QMetaObject and call its invokeMethod() to call the final mem func.
BTW, unlike MFC and other win32 gui, Qt can use UI object in only one thread(the main thread). Make gui operation atomically would not help in that case(at the end of any operation you should call syn method like QWidget::update() ...). In Qt way, you have to separate your logic and gui with signal and slots.[/quote]
I think one of my issues is that some of the operations on my model involve multiple deletes and inserts, and I might be emitting a lot of 'dataChanged' signals... Is there some way to tell the model to stop updating connected views during a long operation, and emit a single 'dataChanged' at the end?
Do I need to inherit from QAbstractItemModel instead to do that? As Gerolf said, I'd get much more liberty and control over thread safety, but it involves some pretty heavy refactoring :([/quote]