С++ - SQLite3 пропускает дескрипторы в многопоточной среде

Я написал простую программу, которая порождает 10 потоков, каждый поток открывает базу данных (общую для всех потоков) или создает ее (с опцией «Write-Ahead Log»), если открытие не удается, создает таблицу в базе данных, а затем в бесконечный цикл, в котором он добавляет одну строку за раз в свою таблицу. Я обнаружил, что программа пропускает около 2 дескрипторов каждые 5 минут, я попробовал инструмент под названием Memory Verify, который сообщает мне, что утечка дескрипторов — это блокировки файлов SQLite3 (строка 34034 в версии 3.7.13), но я не уверен, что ошибка находится в SQLite или в том, как я его использую.

Я не указал какой-либо параметр компилятора для сборки SQLite3, поэтому он построен как многопоточный, и, насколько я понимаю, многопоточный должен нормально работать в моем случае, поскольку каждый поток имеет свое собственное соединение SQLite.

Чтобы открыть или создать базу данных, я использую следующий код:

   bool Create()
   {
      int iFlags = 0;
      iFlags = iFlags | SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_CREATE;
      return sqlite3_open_v2(dbName_sm.c_str(), &pHandle_m, iFlags, 0) == SQLITE_OK;
   }

   bool Open()
   {
      int iFlags = 0;
      iFlags = iFlags | SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX;
      return sqlite3_open_v2(dbName_sm.c_str(), &pHandle_m, iFlags, 0) == SQLITE_OK;
   }

Жесткий цикл в каждом потоке вызывает ExecuteQuery, который подготавливает, шагает и завершает оператор INSERT:

   bool ExecuteQuery(const std::string& statement)
   {
      bool res = Prepare(statement);
      if(!res)
      {
         return false;;
      }
      SQLiteStatus status = Step();
      Finalize();
      res = (ESuccess == status || EDatabaseDone == status);
      return res;
   }

   bool Prepare(const std::string& statement)
   {
      return sqlite3_prepare_v2(pHandle_m, statement.c_str(), -1, &pStmt_m, 0) == SQLITE_OK;
   }

   enum SQLiteStatus { ESuccess, EDatabaseDone, EDatabaseTimeout, EDatabaseError };
   SQLiteStatus Step()
   {
      int iRet = sqlite3_step(pStmt_m);
      if (iRet == SQLITE_DONE)
      {
         return EDatabaseDone;
      }
      else if (iRet == SQLITE_BUSY)
      {
         return EDatabaseTimeout;
      }
      else if (iRet != SQLITE_ROW)
      {
         return EDatabaseError;
      }
      return ESuccess;
   }

   bool Finalize()
   {
      int iRet = sqlite3_finalize(pStmt_m);
      pStmt_m = 0;
      return iRet == SQLITE_OK;
   }

Ребята, вы видите какую-либо ошибку в моем коде или это известная проблема в SQLite? Я пару дней пытался гуглить, но ничего не нашел по этому поводу.

Большое спасибо за Вашу помощь.

С уважением,

Андреа

P.S. Я забыл сказать, что провожу тест на 64-битном ПК с WinXP, компилятор VS2010, приложение скомпилировано в 32-битной версии, версия SQLite 3.7.13...


person Andrea Guardascione    schedule 24.09.2012    source источник
comment
Подумайте, что происходит, когда все ваши потоки запускаются (почти мгновенно), все их вызовы на открытие терпят неудачу, и все они пытаются создать новую базу данных. Начните с обеспечения того, что будет открываться только один поток и, следовательно, создавать базу данных за раз.   -  person mtsvetkov    schedule 24.09.2012
comment
Привет, спасибо за ваш ответ. Я создал глобальный мьютекс и использую его для защиты открытия/создания базы данных, но жесткий цикл все еще пропускает дескрипторы.   -  person Andrea Guardascione    schedule 25.09.2012


Ответы (1)


проверьте, есть ли у вас sqlite3_reset после каждого sqlite3_step, потому что это один из случаев, который может вызвать утечку. после подготовки оператора с помощью sqlite3_prepare и выполнения его с помощью sqlite3_step вам всегда нужно сбрасывать его с помощью sqlite3_reset.

person vinayak jadi    schedule 19.02.2013