2012-03-28 13 views
0

私は現在Linux上のC++でWebクローラ/スパイダーを作成していますが、データベースの更新にいくつか問題があります。私は公平に新しいC/C++、ちょうどFYIです。glibcのメモリ破損libmysqlcppconnでの準備文

データベースの更新は別々のスレッド(pthreadsを使用)で実行されますが、main()で実行すると同じ問題が存在するため、スレッドの内容を何らかの原因として破棄します。

データベースAPIにlibmysqlcppconnを使用しています。

-O2 -Wall -pedanticを使用してgcc 4.4.3(Ubuntu 4.4.3-4ubuntu5.1)でコンパイルしていて、きれいにコンパイルしています。

しかし、以下の関数commitChangesToDatabase()が呼び出されると、std :: map(url_queue)から項目を取り出し、std :: vector(スクロールバー)にスローし、元のstdからその項目を消去します。 :: mapを実行し、std :: vectorを繰り返し処理して、ベクトルの各項目のMySQLプリペアドステートメントを実行します。ここでそれが難しいところです。ランダム

これは、次のいずれか任意のエラー出力なし

  • クラッシュ(なしセグメンテーション違反、無スタックトレース、ノー何も)検出されたglibcのメモリの破損と
  • クラッシュ(ここでは、出力参照:http://pastie.org/private/wlkuorivq5tptlcr7ojg)を
  • レポートMySQLサーバが離れている(例外がキャッチされている)が、試行し続ける(クラッシュしない)

私は単純なexe cuteUpdate()ですが、無駄です。 私は項目を選ぶことでそのステップを削除しようとしましたが、url_queueの最初のループで更新する項目が見つかるたびに更新を実行します。

このアプリケーションの他の機能は、プリペアドステートメント(別のUPDATE)も使用しており、正常に動作します。これらの関数は別々のスレッドでも実行されます。

私はvalgrindを使ってアプリケーションを実行しましたが、率直に言って出力の大部分を理解していないので、それほど助けにならないでしょう - しかし、誰かが出力を望むなら、実行するオプションを教えてください私はそれを提供します。

私はここから進める方法がありません。誰が何が間違っているかの手掛かりを持っていますか?

struct queue_item_t { 
    int id; 
    int sites_id; 
    int priority; 
    int depth; 
    int handler; 
    int state; // 0 = Pending, 1 = Working, 2 = Completed, 3 = Checked 
    double time_allowed_crawl; 

    bool status; 
    bool was_redirected; 

    double time; 
    double time_end; 
    double time_curl; 
    double size; 

    std::string hash; 
    std::string url; 
    std::string file; 
    std::string host; 
}; 

void commitChangesToDatabase() 
{ 
    map< string, queue_item_t >::iterator it, end; 
    sql::PreparedStatement *pstmt; 
    int i = 0; 

    if (!url_queue.size()) { 
     return; 
    } 

    pthread_mutex_lock(&dbCommitMutex); 
    pthread_mutex_lock(&itemMutex); 

    cout << "commitChangesToDatabase()" << endl; 
    pstmt = dbPrepareStatement("UPDATE crawler_queue SET process_hash = NULL, date_crawled = NOW(), url = ?, hash = ? WHERE id = ?"); 

    for (it = url_queue.begin(); it != url_queue.end();) 
    { 
     if (it->second.state == 2) 
     { 
      pstmt->setString(1, it->second.url); 
      pstmt->setString(2, it->second.hash); 
      pstmt->setInt(3, it->second.id); 

      try { 
       pstmt->executeUpdate(); 
       ++i; 

      } catch (sql::SQLException &e) { 
       cerr << "# ERR: SQLException in " << __FILE__; 
       cerr << "(" << __FUNCTION__ << ") on line " << __LINE__ << endl; 
       cerr << "# ERR: " << e.what(); 
       cerr << " (MySQL error code: " << e.getErrorCode(); 
       cerr << ", SQLState: " << e.getSQLState() << ")" << endl; 
      } 

      url_queue.erase(it++); 
     } 
     else { 
      ++it; 
     } 
    } 

    delete pstmt; 

    cout << "~commitChangesToDatabase()" << endl; 

    pthread_mutex_unlock(&itemMutex); 
    pthread_mutex_unlock(&dbCommitMutex); 
} 

// this function is defined in another file but is written here just to show the contents of it 
sql::PreparedStatement *dbPrepareStatement(const std::string &query) 
{ 
    return con->prepareStatement(query); 
} 

編集:

一部は、しかし、私はそれを除外しますが、データベース上で動作し、すべてではなく、反復をコメントアウトしている、問題はurl_queueコレクションを反復処理であると信じているようです。さらに、ここでの反復は、以下に示すように、マップからアイテムを取り出し、ベクトルをスローし、マップから消去し、プログラムのその部分がうまく動作する、元の単純化された(ただし動作​​する)バージョンです。は、データベースが使用されるたびにクラッシュします。

for (it = url_queue.begin(); it != url_queue.end();) 
{ 
    if (it->second.state == 2) 
    { 
     update_item.type = (!it->second.was_redirected ? 1 : 2); 
     update_item.item = it->second; 

     updates.push_back(update_item); 

     url_queue.erase(it++); 
    } 
    else { 
     ++it; 
    } 
} 

編集2:valgrind --leak-check=yesから

出力:http://pastie.org/private/2ypk0bmawwsqva3ikfazw

+0

あなたの質問に 'queue_item_t'の宣言を追加できますか? –

+0

また、反復処理中のコレクションからアイテムを消去しないでください。イテレータを無効にすることがあります。また、イテレータを常に2回増やします。それは意図的な2回目の項目へのジャンプですか? –

+0

コレクションから消去する方法を見つけました。ここで繰り返しているStackOverflowでは、反復ごとにコレクションの.begin()と.end()を評価することを提案しています。申し訳ありませんが、二重増加は、問題に影響しなかった間違いでした。++はfor(;;)内にあるべきではありません。 –

答えて

0

に思え、反復子が不必要に増加されます。最初のループ本体、およびforステートメントで使用されます。このコードでは、反復子endをインクリメントすることは可能ですが、これは問題のある操作であり、問​​題の原因となる可能性があります。

次のループ構造はこのような場合に適している:
it = url_queue.begin();
while(it != url_queue.end()){ //loop body }

+0

私はこの問題が問題ではないことを示すために私の質問を編集しました。私はすでにプログラムのこの部分を犯人にすることを排除しています。 –

+0

OKをクリックします。私は2つ以上のノートを持っていますが、それらが適用可能かどうかはわかりません。最初; 'conn-> prepareStatement()'の出力をチェックするのは良い習慣ですが、失敗する可能性はありますがそれでも使用することはできます。 2番目の: 'url_queue.size()'はmutexで保護されたブロックの中にあるべきです。 – xaero99

0

私はそれはイテレータを台無しに良いアイデアだとは思いません。置き換え:

else { 
     ++it; 
    } 

によって:

else continue; 

またはそれを削除します。

+0

私の質問の最初の編集で書いたように、イテレーターロジックは動作します - valgrindはそれに問題はなく、データベースAPIのエラーだけを表示しますが、アプリケーション内の他の領域で動作します。なぜこの機能ではうまくいかないのでしょうか。イテレータをインクリメントすることは、それを次の要素に進める唯一の方法です。理解しています。 –