Pull to refresh

Comments 46

Ох опять эта гребаная венгерская нотация…
Ох опять эти гребаные египетские скобки…

p.s. не пинаю, просто наболело)
А где вы её увидели?
Честно говоря сам не понял :) может имеласт в виду вот эта строчка?
pFunc p_func;
for (uint i = 0; i < v.size(); i++) {
mysql_close(this->v.at(i));
}

Никогда так не перебирайте коллекции, никогда. Для доступа к элементам есть итераторы. Должно быть так:

for(std::vector::iterator it = this->v.begin(); it != v.end(); ++v)
delete *it;

Ну и в остальных вариантах так же.
Хабр сожрал данные в угловых скобках
std::vector<MYSQL*>::iterator
Вот это важно. Спасибо, поправлю.
Векторы почему-то быстрее перебираются индексами.

Кстати, у вас ошибка. должно быть ++it, да и this как бы не нужен.
И если нетрудно — объясните почему так нужно перебирать? Ведь циклом я определяю лишь число итераций, а для доступа к элементу вектора использую метод at() — где здесь потенциальная ошибка?
Ну одно из первых, это родная реализация библиотеки, в случае итераторов вы имеете возможность поменять контейнер, в вашем случае лучше бы подошел std::list, быстрая вставка и быстрый последовательный перебор.

Я бы посоветовал всегда объявлять свои типы с помощью typedef и придерживаться минимального необходимого набора операций, в данном случае at лежит в самом конец и реализован далеко не во всех контейнерах, тогда как форвард итератор поддерживают все контейнеры.

Код, что выше писал ночью, что-то накосячил :-) как выше сказано ++it вместо ++v и почему-то delete вместо mysql_close использовал, мозг спал.
> и быстрый последовательный перебор.
перебор вектора как раз быстрее, так как процесору проще прекешировать данные из-за чего сокращается количество кеш мисов.
удалять из вектора можно за O(1) перемещая последний элемент на место удаляемого
А можно и:

for (auto it = v.begin(); it != v.end(); ++it)
  delete *it;


А еще лучше:
for_each (v.begin(), v.end(), [&](MYSQL* ptr){ if (ptr) delete ptr;});
v.clear();


А если заюзать «сверхновый» range for (в VS2010 нет его):
for (auto it : v)
{
  if (*it) delete *it;
}

Можно, никто не запрещает. Но опять же if(*it) не требуется, ибо стандарт говорит о том, что delete 0, не вызовет ошибки, это допустимая операция, ничего не будет удалено, да и к примеру в boost давно это используется :-)
Хм… не знал — обязательно почитаю. Ну а если хранить shared_ptr на MYSQL, то можно и обойтись v.clear().
но переопределенные операторы new/delete ничего не гарантируют.
> устанавливаем число процессов одновременных

Мастер Йода О_о?
Будьте осторожны, тут есть небольшая архитектурная ошибка. Если между ConnectAllThreads() и run() пройдет продолжительное время (в конфигурации MySQL по умолчанию — 28800 секунд) то запросы не выполнятся т.к. соединения разорвется по wait_timeout.
У меня такая же обертка для .Net, я это решаю 'select 1;'
Согласен. MySQL откинет «бездействующие» коннекты по умолчанию через 28800 секунд. Но в моём случае wait_timeout точно не пройдёт.
А чем вас так прельстила асинхронность на стороне MySQL?
В чем проблема работать с базой синхронно, но в отдельных потоках на клиенте?
Или тут больше интерес исследовательского характера?
Честно говоря пока, как вы написали интерес скорее «исследовательского» характера. Пробовал и многопоточно реализовывать (демон на PHP) — но вот сейчас решил попробовать таким путём пойти. Ну и в ресурсах естественно тут преимущество.
Посмотрите gearMan — то что Вам нужно.
интересная идея скрестить либэвент и либмускуль.
это можно было реализовать и на чистом си.
Какая-то сомнительная асинхронность. Или вы утверждаете, что mysql_real_connect() и mysql_read_query_result() неблокирующие функции?
А что какие объекты СУБД они блокируют по-вашему?
СУБД тут не причем.

Блокирующие функции в смысле блокирующие выполнение программы на неопределенный срок. Ведь идея асинхронности в том и состоит, чтобы заменить блокирующие операции неблокирующими. Пока потенциально долгая операция выполняется, программа занимается чем-то еще.

Я подозреваю, что mysql_real_connect() внутри себя делает connect() на сокете. А для установления соединения нужно обменяться несколькими сообщениями с удаленным сервером. Сеть может лагать; connect может занять много времени и это не гипотетическая ситуация а вполне реальная.

Это то что лежит на поверхности. А еще например если имя хоста надо ресолвить, то внутри mysql_real_connect() должен быть запрос к DNS, и врядли он выполняется асинхронно.
/*
Send the query and return so we can do something else.
Needs to be followed by mysql_read_query_result() when we want to
finish processing it.
*/

int STDCALL mysql_send_query(MYSQL* mysql, const char* query, ulong length)
{
DBUG_ENTER(“mysql_send_query”);
DBUG_RETURN(simple_command(mysql, COM_QUERY, (uchar*) query, length, 1));
}
…
my_bool STDCALL mysql_read_query_result(MYSQL *mysql)
{
return (*mysql->methods->read_query_result)(mysql);
}
Ну и что? Скопируйте уже весь код libmysqlclient, приведенного кода явно не достаточно.

Disclaimer: я не особо знаком с кодом libmysqlclient. Честно говоря я туда вобще не заглядывал. А вот libev/libevent знаком хорошо. Сокет считается готовым для чтения как только оттуда можно прочитать хотя бы один байт. Соответственно ваш обработчик будет вызван еще до того, как ответ от mysql сервера будет передан полностью. Обработчик вызовет mysql_read_query_result(), которая заблокируется до тех пор, пока не получит тело ответа целиком.

Ну и где здесь асинхронность, извините?
Есть подозрение, что все не очень хорошо:
  • mysql_connect() is blocking only
  • mysql_send_query() doesn’t seem to handle EAGAIN (send buffer is full)
  • I don’t know if mysql_use_result() can be return EAGAIN easily

да, согл — но это уже лучше чем ничего.
mysql_use_result для асинхронного выполнения запросов не совсем подходит.

мой коллега пошел по более простому пути,
он переопределил функцию my_init() — т.е. определил свою с этим же именем и написал частично свой код, в которой реализовал неблокируемый mysql_connect() и реализовал асинхронность с помощью коротин (pcl).
>mysql_connect() is blocking only
можно его переписать на неблокирующий режим,
можно предварительно открыть не блокируемый сокет и самостоятельно, но к сожалению mysql_connect() — это больше чем просто коннект. Проще не трогать,
да и вообще, всегда и даже в моем маленьком примере первоначально создается пул коннекций, а потом параллельно начинаем выполнять запросики.
>I don’t know if mysql_use_result() can be return EAGAIN easily
состояние EAGAIN можно отлавливать непосредственно в libev,
выдаю два запроса
data[0].sql = «select sleep(3)»;
data[1].sql = «SELECT `crc` FROM hidden_contacts LIMIT 5»;


результат:
$ ./test test.c:110 connected m->net.fd = 3
test.c:110 connected m->net.fd = 4
call timer cb…
send query 'select sleep(3)'
send query 'SELECT `crc` FROM hidden_contacts LIMIT 5'
sent queryes
start watcher id=1
a result for m->net.fd = 4
sql_p2_cb
3272202
6444339
6877213
7407359
7672453
free stor result id=1
start watcher id=0
a result for m->net.fd = 3
sql_p1_cb
query is Ok res=0
free stor result id=0
А что какие объекты СУБД они блокируют по-вашему?
Извиняюсь. Сглупил. Не сразу понял что автор пишет само приложение на c++. Думал изобретает модуль для php.
Сам я по заголовку подумал, что для nodejs сейчас будет статья. Жаль.
Так я о Вашей библиотеке знаю. Но статье с примерами жизненного использования в том числе установку, описание я бы обрадовался.
Я надеюсь в течении пары недель закончить с высокоуровневой обёрткой и вплотную займусь документацией и пиаром :) К тому времени, скорее всего, будет уже поддержка prepared statements, а значит по сути всё API libmysqlclient будет покрыто.
Сколько же у Вас this. Вы, наверное, из питона пришли?
А что произойдет если в тот же самый коннект отправить новый запрос, не дождавшись ответа на предыдущего?
Sign up to leave a comment.

Articles