Как стать автором
Обновить

Комментарии 23

Разве в этом случае (статическая переменная внутри метода класса) экземпляр подключения будет создан в той выделенной нити? Конструктор будет вызван при первом вызове GetInstance, что произойдёт в конструкторе OurThread::OurThread(), который исполняется не в новой нити.
как бы первый вызов идет в main.cpp после инициализации параметров подключения
Я не так всё понял. Я подумал, что OurThread это выделенная нить, которая занимается только обработкой SQL-запросов.
Не красиво :) У вас класс DatabaseAccessor, который по сути должен заниматься лишь обработкой sql запросов и возвратом результата, содержит часть «бизнес логики». Добавьте еще с десяток методов аля ValidateUser и ваш синглтон превратится в большую помойную яму. К тому же QMetaObject::invokeMethod — очень медленно.

Если вы уж и пошли по пути синглтона, то я на вашем месте предложил такую модель:
— Класс DatabaseAccessor — синглтон, который открывает соединение с базой при первом обращении.
— Класс DatabaseAccessor наследует QThread и все обращение к базе происходят в контексте потока DatabaseAccessor.
— Класс DatabaseAccessor имеет встроенную очередь запросов, которая содержит пару: запрос + коллбек (указатель на метод класса). Ну и мьютекс, который защищает эту очередь от race conditions.
— Если какой-нибудь сторонний поток желает послать какой-то запрос и получить данные, он вызывает метод DatabaseAccessor и передает туда собственно текст запроса и коллбек-обработчик(указатель на свой метод), который вызван DatabaseAccessor'ом после получения результата запроса.
— Запрос и колбек ставятся в очередь на исполнение. Запрос будет обработан при следующей итерации внутреннего цыкла обработчики в потоке DatabaseAccessor.
— После получения результата запроса, DatabaseAccessor вызывает коллбек, и класс, который запрашивал данные получает их :)

Тут есть пара небольших минусов: коллбек будет вызван в контексте потока DatabaseAccessor, посему подобные коллбеки стоит делать очень легковесными, дабы они не отбирали время у потока DatabaseAccessor, ведь для него — это очень критично.
ну собственно про бизнес логику я писал, что можно реализовать один мега-метод который будет делать тот же селект и возвращать все что вернула база…
Если у нас 5-10 запросов в приложении, которые используются очень часто и при этом необходимо выполнять некую бизнес-логику, то лучшее ее сделать в этой прослойке, тем самым мы не будем беспокоиться о валидации/преобразовании вообще…
По поводу инвокМетода… это можно сделать, но только в случае когда мы делаем общий большой селект, иначе придется развлекаться с большим количеством разнообразных параметров…
Соглашусь.

>коллбек будет вызван в контексте потока DatabaseAccessor, посему подобные коллбеки стоит делать очень легковесными

Если это будет обычный сигнал/слот, то проблем не будет. DatabaseAccessor сформировал и сохранил результат, кинул сигнал-оповещение о готовности и пошёл работать дальше.

Кстати такая схема очень актуальна для GUI, поскольку:
* Все SQL-запросы идут последовательно и в одном треде, а если это ещё и sqlite — это очень полезно.
* Основной поток делает всё асинхронно и не ждёт завершения запроса, интерфейс не «подвисает».

Кстати
> sleep(1);
Это точно дурной тон.
не спорю мслип это дурной тон… но это в некоторых случаях единственный способ не заграбастать все время процессора (например на Эльбрусах так, на некоторых ядрах линукса когда-то тоже было так)
Так а зачем заниматся поллингом? Можно ведь просто использовать другой механизм синхронизации, например Event + WaitForSingleObject на win32.
ну вот это точно использовать не стоит, так как это системные вещи, а не кутешные…
Что точно не нужно использовать, так это msleep(1). Такой подход как раз нужно использовать, потому что он идеологически верен и не сжирает ресурсы. Если вас расстраивают ивенты — семулируйте их в pthreads с помощью conditional variables и мьютексов. А если хотите отделаться чисто кутешными штуками — атомарные операции и мьютекс в помощь :)
ну вот это лучше;) но честно говоря при сравнении производительности решения на мутексе и решения с мслипом (оговорюсь что проверялась именно ситуации тупого ожидания одного события с необходимостью как можно больше процессорного времени отдать другим потокам) на куте3 (опять же на куте4 не проверял, вполне может быть что работа мутексов улучшилась) разница была в пределах погрешности…
минусующий — ты считаешь что в при использовании кроссового фреймворка надо пользоваться системными вещами вместо предлагаемых фреймворком?
Вот сначала таких слипов понапишут. Потом окажется что они слишком жрут батарею у ноутов, придется с PowerTop мучатся, искать все эти слипы и удалять.
>на некоторых ядрах линукса когда-то тоже было так
Этож на каком ядре не было select/poll?

Но вообще работая под QT желательно использовать средства мультиплексирования, которые уже идут в фреймворке, прежде всего сигналы.
честно не помню на каких были проблемы (я не говорю что вообще все было плохо, просто с мслипом оно работало так как задумывалось а не так как само приложение захочет), но помню что в те времена когда я работал с Эльбрусом (тогда еще это был куте3) такие проблемы были как на МСВС на Эльбрусе, так и на обычном линухе на интеле…

З.Ы. может быть в настоящее время это лишняя предосторожность, не спорю;)

З.З.Ы. кстати всегда интересовало как можно с помощью истинно кутешных средств типа сигналов сделать нормальный воркфлоу программы… на ум пришло только сохранение состояния на каждом чихе и излишнее дробление архитектуры
нормальный или удобный воркфлоу?

Нужно понимать, что сам гуи будет асинхронным (ввод пользователя) и нормального (последовательного) кода не будет.
что гуи асинхронен это понятно… меня интересует как организовать воркфлоу какого-нибудь алгоритма с большим количеством асинхронных вызовов (ну та же работа с бд)…
> как организовать воркфлоу какого-нибудь алгоритма с большим количеством асинхронных вызовов (ну та же работа с бд)
Отличная тема для поста на хабре! :)

Но только стоит рассмотреть ещё работу с сетью.
ну как только придумаю/найду что-либо приемлемое, так напишу, но если кто знает буду рад почитать
> 1. Не париться и делать в каждом потоке свое подключение
> 2. Обратиться к старому доброму паттерну Singleton

3. Coroutines/продолжения
x. (у кого-нибудь есть еще варианты?)

> Паттерн Singleton подразумевает, что у нас может быть только один объект определенного класса и при любом обращении будет исполняться код в рамках этого объекта. Далее в статье мы слегка отступим от канонической формы этого паттерна, но об этом позже.

Вопрос: зачем городить объект, если можно обойтись без него? Так будет короче, яснее.

И кстати, по всей видимости, синглтон не является данными, скорее это коданные: синглтон нельзя сконструировать самому, его внутренняя структура скрыта, etc, и поэтому это не объект вовсе. :) (Сравниваем со структурами данных, там все наоборот: структура известна, сконструировать можно самому, etc.) Вопрос, конечно, философский.
всмысле обойтись без объекта? вы имеете в виду сделать статик класс? в таком случае мы не сможем сделать то что описано в заключении…
1. Не париться и делать в каждом потоке свое подключение
Вы сравнивали производительность с 2.? Я думал, что connection pooling довольно эффективно сглаживает затраты.
если у нас 3к потребителей БД, то делать 3к подключений просто неразумно… а сделать например 100 подключений (по подключению на 30 потребителей) уже будет очень хорошо…
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории