Qt: Използване на нишка за конструиране на джаджи

В моето приложение имам QWidget MyWidget, което трябва да конструирам след щракване с PushButton. Сега MyWidget е тежка джаджа с много дъщерни компоненти и тези дъщерни компоненти също имат много компоненти. Сега, когато се опитам да конструирам джаджата след натискане на бутон, отнема понякога, за да се появи джаджата, което не ми харесва. Така че трябва да конструирам джаджата, преди бутонът да щракне и да държа екземпляр под ръка. Нишката изглежда идеална за тази работа. Но имам малко представа за Qt Thread.

Сега може ли някой да ми предложи как да подходя към проблема и какво може да се направи, за да се реши проблемът?

Забележка: За да кажа изрично защо моята джаджа е толкова тежка, в MyWidget имам 7*24 джаджи, всяка от които има подредена джаджа с две джаджи, подредени върху нея, всяка с бутон.


person Shakib Ahmed    schedule 27.07.2014    source източник
comment
Тези детски компоненти ваши собствени джаджи ли са? Какво отнема толкова време при инициализацията им? Защо правите тази инициализация в конструкторите?   -  person Kuba hasn't forgotten Monica    schedule 28.07.2014
comment
Каква алтернативна идея мога да използвам?   -  person Shakib Ahmed    schedule 28.07.2014
comment
В този момент трябва да покажете кода. Код, който можем да компилираме и тестваме. Код, който възпроизвежда вашата бавност   -  person Kuba hasn't forgotten Monica    schedule 28.07.2014


Отговори (2)


Qt не поддържа създаване на уиджети в не-gui нишки. Причината е проста: конструкторите на джаджи имат свободен достъп до несигурни за нишките API, които могат да бъдат използвани от основната нишка по всяко време. Така дори и да изглежда, че работи, това наистина е грешка във вашия код, ако го направите.

Най-общо казано, във вашите тежки класове на джаджи, паралелизирайте неграфичните, дълго изпълняващи се задачи за инициализация. Можете да използвате QtConcurrent::run за тази цел. Разбира се, за тази цел трябва да направите вашата реализация нишково безопасна.

Трябва също така да се уверите, че абсолютно, категорично не използвате никакви блокиращи API в кода на вашия уиджет. Никой GUI код като цяло, но нито един конкретен конструктор на джаджа, никога не трябва:

  • изчакайте всички достъпи до файлове да приключат,

  • изчакайте всяка работа в мрежа да приключи,

  • изчакайте достъпът до базата данни да приключи,

  • използвайте всякакви waitForXxxx методи в Qt,

  • използвайте неща, които могат да имат индиректен достъп до файловата система, като QSettings.

person Kuba hasn't forgotten Monica    schedule 28.07.2014
comment
Ако ми казвате да направя потребителския конструктор нишково безопасен, аз го направих нишково безопасен. По-късно потребителският интерфейс извиква някои класове на контролера, класовете на конструктора от името на контролера имат достъп до базата данни. Достатъчно добър ли е? - person Shakib Ahmed; 28.07.2014
comment
@ShakibAhmed Конструкторът QWidget по дефиниция не е безопасен за нишки, не можете да го направите такъв. Това, от което се нуждаете, за да направите нишката безопасна, е дълготрайният код, който извършва неграфичната инициализация, която вероятно работи дълго. След това такъв код може да бъде изпълнен чрез QtConcurrent::run - той ще се изпълнява в работна нишка. Трябва да ни кажете какво отнема толкова време във вашите конструктори. - person Kuba hasn't forgotten Monica; 28.07.2014
comment
За да кажа изрично, в моята джаджа имам 7*24 джаджи, всяка от които има подредена джаджа с две джаджи, подредени върху нея, всяка от които има бутон. - person Shakib Ahmed; 28.07.2014
comment
@ShakibAhmed Страхотно. Покажете ни прост пример за функция main, която създава това, в идеалния случай с някакъв времеви код, който да покаже времето за това. Не трябва да е повече от мисля общо 40 реда. Иначе тази дискусия е във вакуум. - person Kuba hasn't forgotten Monica; 28.07.2014

На първо място Qt и почти всяка друга рамка (най-накрая тези, които познавам), която се занимава с елементи на потребителския интерфейс, имат много специфични изисквания за поддръжка на много нишки.

Qt се нуждае от всеки QObject наследник (като QWidget), за да бъде присвоен на нишка (вижте QObject::thread()).

Това свойство се присвоява автоматично при създаване на обект към нишката, в която се създава обектът.

Проблемът с това е, че Qt също изисква приспособлението да получава събития само ако принадлежи към същата нишка, където се изпълнява цикълът на събитията (и е правилно добавено към родителския уиджет).

Ако създавате своята огромна джаджа в някаква различна нишка, тя няма да получава събития, докато не бъде преместена в нишката с цикъла на събитията и повторно родителска.

Можете да преместите QWidget в друга нишка, като извикате QObject::moveToThread() метод. Да предположим, че mParent е вашето QMainWindow и mMyWidget е вашата огромна джаджа:

mMyWidget->setParent(0);
mMyWidget->moveToThread(mParent->thread());
mMyWidget->setParent(mParent); // change this to match your layout requirements.

Проверете QWidget::setParent() за повече информация.

Създаването на вашата огромна джаджа не трябва да се извършва в същата нишка като основната ви нишка. Това е оставено като упражнение, тъй като е почти ясно и Qt документите са обширни за нишки.

Внимавайте, ако бутонът ви е натиснат, преди да е приключило създаването на джаджата: трябва да синхронизирате достъпа до променливата, за да избегнете недефинирано поведение.

Това е поглед на проблема на много високо ниво, но е начало и тъй като не предоставихте никакъв код, давам ви минимум ;D

Друг вариант (и вероятно по-добър) е да погледнете защо създаването на вашата джаджа отнема толкова много време, но без повече информация не мога да помогна.

person Vinícius Gobbo A. de Oliveira    schedule 27.07.2014
comment
Мисля, че предполагате много, когато предполагате, че не е недефинирано поведение за създаване на QWidget в друга нишка. Що се отнася до мен, всеки опит да го направите е бъг, който рано или късно ще ви ухапе. Ако вашето инстанциране на джаджа отнема толкова много време, трябва да прехвърлите времеемките неща, които не са GUI, на помощник, който се изпълнява чрез QtConcurrent::run. Това е най-доброто, което можете да направите. Разбира се, някои графични неща могат да бъдат направени от всяка нишка. Например рисуване или зареждане на QImages. - person Kuba hasn't forgotten Monica; 28.07.2014
comment
Бих искал да греша тук, разбира се. Моля, покажете някакво потвърждение (документация или съответен анализ на изходния код на Qt), което показва, че създаването на джаджи в произволна нишка е ОК. - person Kuba hasn't forgotten Monica; 28.07.2014
comment
В момента приемам този отговор като сериозно лош съвет. - person Kuba hasn't forgotten Monica; 28.07.2014
comment
Когато започнах да уча Qt, тествах това. Исках да науча за нишки и други неща в Qt и бях наистина ужасен програмист (току-що започнах дипломния си курс), така че си играх (много!) с тези ужасни неща. Съгласен съм с вас, това е наистина лош съвет, но успях да го накарам да проработи веднъж и с подробностите, които потребителят даде там, няма много какво да се каже. - person Vinícius Gobbo A. de Oliveira; 28.07.2014
comment
Проблемът е, че вашият код, работещ без срив за какъвто и да е период от време, не е индикация, че работи. Няма начин да тестваме дали работи. Резултатите от тестове, извън много тесни обстоятелства, не могат да се приемат като индикация за липса на грешки, те могат да се използват само за показване на наличие на грешки, когато тестовете са неуспешни. Правилно проектираният тест винаги ще се провали, когато конструирате QWidget в не-gui нишки. Въпросът е само да се напише такъв тест. - person Kuba hasn't forgotten Monica; 28.07.2014