0

私は作業中のC ++デーモンを持っています。問題は、デーモンが月に 1 ~ 2 回クラッシュすることです。以下の GDB 出力からわかるように、デーモンがunsigned intコンテナーでセッション ID を検索するときに発生しますstd::map <unsigned int const, SessionID*>。私は問題を再現できず、おそらくユーザーからのデータに問題があると思います (std::sting cookie_ssid予期しないデータがあり、変換後にstrtoul何か問題が発生している可能性があります) (これは正しい取得方法ではないことを知っておいunsigned intてください) 。

.coreデーモンがクラッシュした後、ファイルしかありません。でその問題を参照してくださいif (!_M_impl._M_key_compare(_S_key(__x), __k))。この問題を解決する方法はありますか? どうもありがとう。

GDB 出力:

#0  std::_Rb_tree<unsigned int, std::pair<unsigned int const, SessionID*>, std::_Select1st<std::pair<unsigned int const, SessionID*> >, std::less<unsigned int>, std::allocator<std::pair<unsigned int const, SessionID*> > >::find (this=0x529df15c, __k=@0x7f5fab7c)
    at stl_tree.h:1376
##########
1376            if (!_M_impl._M_key_compare(_S_key(__x), __k))
##########
#0  std::_Rb_tree<unsigned int, std::pair<unsigned int const, SessionID*>, std::_Select1st<std::pair<unsigned int const, SessionID*> >, std::less<unsigned int>, std::allocator<std::pair<unsigned int const, SessionID*> > >::find (
    this=0x529df15c, __k=@0x7f5fab7c) at stl_tree.h:1376
#1  0x0805e6be in TR::find_session (this=0x529df110,
    cookie_ssid=@0x47ef3614, ptr_to_ptr_session=0x7f5fac7c)
    at stl_map.h:542

関数TR::find_sessionは次のようにリリースされます。

bool TR::find_session ( const std::string &cookie_ssid, SessionID **ptr_to_ptr_session )
{
    unsigned int uint_sessionid = std::strtoul ( cookie_ssid.c_str(),NULL,0);

MUTEX_map_sessionids.lock_reading();
    std::map<unsigned int, SessionID*>::iterator it_sessionids = map_sessionids.find( uint_sessionid );

    if ( it_sessionids != map_sessionids.end() )
    { // exists
        *ptr_to_ptr_session = it_sessionids->second;
        MUTEX_map_sessionids.unlock();
        return true;
    }

MUTEX_map_sessionids.unlock();  
return false;   
}

EDIT 別のスレッドで動作するクリーンアップ機能 (1 分または 5 分に 1 回)。コメントで要求されたとおりです。この機能についてはよくわかりません。多分そのバギー....

void TR::cleanup_sessions () // not protected from multithread using! used only at one thread
{
std::list<SessionID*> list_to_clean; // tmplary store sessions to delete

MUTEX_map_sessionids.lock_reading();
std::map<unsigned int, SessionID*>::iterator it_sessionids = map_sessionids.begin();
MUTEX_map_sessionids.unlock();

while ( true )
{
    MUTEX_map_sessionids.lock_writing();
    if (it_sessionids == map_sessionids.end() )
    {
        MUTEX_map_sessionids.unlock();
        break;
    }

    SessionID *ptr_sessionid = it_sessionids->second;

    time_t secondsnow = time (NULL);

    ptr_sessionid->MUTEX_all_session.lock_reading();
    time_t lastaccesstime = ptr_sessionid->last_access_time;
    size_t total_showed = ptr_sessionid->map_showed.size(); 
    ptr_sessionid->MUTEX_all_session.unlock();


    if ( lastaccesstime and secondsnow - lastaccesstime > LOCALSESSION_LIFETIME_SEC ) // lifetime end!
    {
        // delete session from map
        map_sessionids.erase( it_sessionids++ ); // Increments the iterator but returns the original value for use by erase
        MUTEX_map_sessionids.unlock();              


        list_to_clean.push_back ( ptr_sessionid ); // at the end
    }
    else if ( total_showed == 0 and secondsnow - lastaccesstime > 36000 ) // not active for N secontes
    {
        map_sessionids.erase( it_sessionids++ ); // Increments the iterator but returns the original value for use by erase
        MUTEX_map_sessionids.unlock();

        // add pointer to list to delete it latter
        list_to_clean.push_back ( ptr_sessionid ); // at the end            
    }
    else
    {
        ++it_sessionids; // next
        MUTEX_map_sessionids.unlock();              
    }

}

// used? pause
if ( !list_to_clean.empty() ) 
{
    //sleep(1);
}

// cleanup session deleted from working map
while ( !list_to_clean.empty() )
{
    SessionID *ptr_sessionid_to_delete = list_to_clean.front();
    list_to_clean.pop_front();

    ptr_sessionid_to_delete->MUTEX_all_session.lock_writing(); // protected lock session mutex. can not delete session if its already locked. (additational protection)
    ptr_sessionid_to_delete->cleanup();
    delete ptr_sessionid_to_delete;
}

}

繰り返しごとにわかるように、map_sessions をロック/ロック解除します。これは、この時点で他のスレッドが新しいセッションとそのクリティカルを検索/挿入するためです。これは、ユーザーが待機できないためです

4

1 に答える 1

2

マップを変更すると、そのマップ内のイテレータが無効になる可能性があることに注意してください。あなたが持っている:

MUTEX_map_sessionids.lock_reading();
std::map<unsigned int, SessionID*>::iterator it_sessionids = map_sessionids.begin();
MUTEX_map_sessionids.unlock();

このロック解除の直後に、他のスレッドがロックを取得し、 を無効it_sessionidsにする何らかの操作を行う可能性があります。これにより、後続のコードがマップを破損し、後でクラッシュにつながる状態になります。

イテレータの存続期間全体にわたってロックを取得して保持する必要があります。リーダー/ライター ロックがあるように見えるので、マップを変更したい場合は読み取りロックをずっと保持し、マップを変更したいときに書き込みロックにアップグレードし、変更後すぐに読み取りロックに戻す必要があります。読み取りロックを長時間保持すると、読み取りロックのみを必要とする他のスレッドではなく、書き込みロックを取得したい他のスレッドのみがブロックされます。

コメントで質問に答える:

  1. 長期間ロックを保持できない場合、長期間有効なイテレーターを持つことはできません。できることの 1 つは、マップ内のどこにいるのかをときどき思い出し、ロックを解放して (他のスレッドにチャンスを与え、反復子を無効にする)、ロックを再取得して、ほぼ同じ時点で新しい反復子を作成することです。ループの途中で次のようなものを追加できます。

    if (++count > limit) { // only do this every Nth iteration
        unsigned int now_at = it_sessionids->first;
        MUTEX_map_sessionids.unlock();
        // give others a chance
        MUTEX_map_sessionids.lock_reading();
        it_sessionids = map_sessionids.lower_bound(now_at);
        count = 0; }
    
  2. ロックを読み取り専用から読み取り/書き込みにアップグレードすることは、実装がサポートしていない可能性があるリーダー/ライター ロックに対する基本的な操作です。そうでない場合は、運が悪く、ライターロックをずっと保持する必要があります。

于 2012-12-06T19:58:44.913 に答える