3

POSIXスレッドとC++を使用して、一度に1つずつ安全に実行できる「挿入操作」があります。

pthread_join を使用して挿入を待機しているスレッドが複数ある場合は、終了時に新しいスレッドを生成します。それらはすべて一度に「スレッド完了」シグナルを受け取り、複数の挿入を生成しますか、または「スレッド完了」シグナルを最初に受信したスレッドが新しいスレッドを生成し、他のスレッドが新しいスレッドを作成するのをブロックすると想定しても安全ですか。

/* --- GLOBAL --- */
pthread_t insertThread;



/* --- DIFFERENT THREADS --- */
// Wait for Current insert to finish
pthread_join(insertThread, NULL); 

// Done start a new one
pthread_create(&insertThread, NULL, Insert, Data);

返信ありがとうございます

このプログラムは基本的に、ソケットを介してクライアントからの要求を受け取る巨大なハッシュ テーブルです。

新しいクライアント接続ごとに新しいスレッドが生成され、そこから複数の操作 (具体的にはルックアップまたは挿入) を実行できます。検索は並行して実行できます。ただし、挿入は単一のスレッドに「再結合」する必要があります。クライアントの新しいスレッドを生成せずにルックアップ操作を実行できると言えますが、サーバーがロックされて新しいリクエストがドロップされるまでに時間がかかる場合があります。この設計では、システム コールとスレッドの作成を可能な限り最小限に抑えようとしています。

しかし、最初に考えた方法では安全ではないことがわかったので、何かを一緒に石畳にできるはずです

ありがとう

4

9 に答える 9

3

pthread_join の opengroup.orgから:

同じターゲット スレッドを指定して pthread_join() を複数同時に呼び出した場合の結果は未定義です。

したがって、以前の insertThread に複数のスレッドを結合するべきではありません。

まず、C++ を使用しているので、boost.threadをお勧めします。これらはスレッドの POSIX モデルに似ており、Windows でも機能します。また、関数オブジェクトをより簡単に使用できるようにすることで、C++ で役立ちます。

第二に、次のスレッドを開始する前に前のスレッドが終了するのを常に待たなければならないのに、要素を挿入するために新しいスレッドを開始する必要があるのはなぜですか? マルチスレッドの古典的な使用法ではないようです。

ただし...これに対する1つの古典的な解決策は、1つのワーカースレッドがイベントキューからジョブを取得し、他のスレッドが操作をイベントキューに投稿することです。

多かれ少なかれ現在の状態を維持したいだけの場合は、次のようにする必要があります。

  • のような条件変数を作成しますinsert_finished
  • 挿入を行いたいすべてのスレッドは、条件変数を待ちます。
  • 1 つのスレッドが挿入を完了するとすぐに、条件変数を起動します。
  • 条件変数にはミューテックスが必要なため、すべての待機中のスレッドに通知するだけで挿入を開始できますが、一度に 1 つのスレッドしかミューテックスを取得できないため、すべてのスレッドが順番に挿入を行います。

ただし、同期がその場限りの方法で実装されないように注意する必要があります。これは と呼ばれinsertているので、データ構造を操作したいと思うので、データ構造へのアクセスとすべてのクライアントの間で同期を共有するのではなく、最初にスレッドセーフなデータ構造を実装することをお勧めします。insertまた、適切な同期が必要になるだけでなく、より多くの操作があると思われます...

于 2009-01-10T15:26:58.717 に答える
2

Single Unix Specifcation によると、「同じターゲット スレッドを指定する pthread_join() への複数の同時呼び出しの結果は未定義です。」

タスクを取得するために単一のスレッドを達成する「通常の方法」は、条件変数を設定することです (関連するミューテックスを忘れないでください): アイドルスレッドは pthread_cond_wait() (または pthread_cond_timedwait()) で待機し、スレッドが作業が終了すると、pthread_cond_signal() でアイドル状態の 1 つを起こします。

于 2009-01-10T15:21:30.647 に答える
1

はい、ほとんどの人が推奨する最善の方法は、ワーカースレッドがキューから読み取るようです。以下のいくつかのコードスニペット

    pthread_t       insertThread = NULL;
    pthread_mutex_t insertConditionNewMutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_t insertConditionDoneMutex    = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t  insertConditionNew      = PTHREAD_COND_INITIALIZER;
    pthread_cond_t  insertConditionDone     = PTHREAD_COND_INITIALIZER;

       //Thread for new incoming connection
        void * newBatchInsert()
        {
           for(each Word)
           {
                            //Push It into the queue
                            pthread_mutex_lock(&lexicon[newPendingWord->length - 1]->insertQueueMutex);
                                lexicon[newPendingWord->length - 1]->insertQueue.push(newPendingWord);
                            pthread_mutex_unlock(&lexicon[newPendingWord->length - 1]->insertQueueMutex);

           }

                    //Send signal to worker Thread
                    pthread_mutex_lock(&insertConditionNewMutex);
                        pthread_cond_signal(&insertConditionNew);
                    pthread_mutex_unlock(&insertConditionNewMutex);

                    //Wait Until it's finished
                    pthread_cond_wait(&insertConditionDone, &insertConditionDoneMutex);

        }


            //Worker thread
            void * insertWorker(void *)
            {

                while(1)        
                {

                    pthread_cond_wait(&insertConditionNew, &insertConditionNewMutex);

                    for (int ii = 0; ii < maxWordLength; ++ii)
                    {                   
                            while (!lexicon[ii]->insertQueue.empty())
                            {

                                queueNode * newPendingWord = lexicon[ii]->insertQueue.front();


                                lexicon[ii]->insert(newPendingWord->word);

                                pthread_mutex_lock(&lexicon[ii]->insertQueueMutex);
                                lexicon[ii]->insertQueue.pop();
                                pthread_mutex_unlock(&lexicon[ii]->insertQueueMutex);

                            }

                    }

                    //Send signal that it's done
                    pthread_mutex_lock(&insertConditionDoneMutex);
                        pthread_cond_broadcast(&insertConditionDone);
                    pthread_mutex_unlock(&insertConditionDoneMutex);

                }

            }

            int main(int argc, char * const argv[]) 
            {

                pthread_create(&insertThread, NULL, &insertWorker, NULL);


                lexiconServer = new server(serverPort, (void *) newBatchInsert);

                return 0;
            }
于 2009-01-11T11:29:11.770 に答える
0

他の人は、これが未定義の振る舞いをしていることをすでに指摘しています。タスクを実行するための本当に最も簡単な方法(コードの一部を実行するスレッドを1つだけ許可する)は、単純なミューテックスを使用することです-そのコードを実行するスレッドは相互に排他的である必要があり、そこでミューテックスが登場しましたその名前 :-)

コードを特定のスレッド(Java AWTなど)で実行する必要がある場合は、条件変数が必要です。ただし、このソリューションが実際に効果を発揮するかどうかをよく考える必要があります。「挿入操作」を1秒間に10000回呼び出す場合、必要なコンテキストスイッチの数を想像してみてください。

于 2009-01-10T16:10:10.567 に答える
0

理想的には、異なる操作を実行する場合でも、単一のプロセスに複数のスレッドプールが必要ではありません。スレッドの再利用可能性は重要なアーキテクチャ定義であり、Cを使用すると、メインスレッドでpthread_joinが作成されます。

もちろん、C ++スレッドプール(別名ThreadFactory)の場合、スレッドプリミティブを抽象化して、渡された関数/操作タイプを処理できるようにするという考え方です。

典型的な例は、接続プールと、接続を処理してさらに処理するスレッドプールを持つWebサーバーですが、すべてが共通のスレッドプールプロセスから派生しています。

概要:メインスレッド以外の場所ではPTHREAD_JOINを避けてください。

于 2012-01-01T22:25:47.527 に答える
0

挿入をハッシュテーブルにシリアル化したいと思うようです。

このためには、新しいスレッドを生成しないで、ロックが必要です。

于 2009-01-10T17:27:01.660 に答える
0

何かを挿入するたびに挿入スレッドを再作成しているため、非常に非効率に見える説明から。スレッドの作成コストは0ではありません。

この問題のより一般的な解決策は、キューで待機する挿入スレッドを生成することです(つまり、ループが空のときにループ内でスリープ状態になります)。次に、他のスレッドが作業項目をキューに追加します。挿入スレッドは、キューのアイテムを追加された順序で(または、必要に応じて優先順位に従って)選択し、適切なアクションを実行します。

キューへの追加が保護されていることを確認するだけで、一度に1つのスレッドだけが実際のキューの変更にアクセスでき、挿入スレッドはビジー待機を行わず、キューに何もないときにスリープします。 (条件変数を参照)。

于 2009-01-11T07:49:46.003 に答える
0

新しいルックアップをロックせずに挿入をサポートする、私が見つけた唯一のライブラリ - Sunrise DD (そして、同時挿入をサポートしているかどうかはわかりません)

ただし、Google のスパース ハッシュ マップから切り替えると、メモリ使用量が 2 倍以上になります。ルックアップはかなり頻繁に行われるべきではないので、両方の利点を組み合わせた独自のライブラリを作成しようとするのではなく、変更が安全に行われている間、ルックアップを一時停止するテーブルをロックするだけです。

再度、感謝します

于 2009-01-10T16:56:01.347 に答える
0

あなたが今言及したように、挿入と並行していくつかのルックアップを持つハッシュテーブルを使用しているため、同時ハッシュテーブルを使用できるかどうかを確認することをお勧めします.

要素を同時に挿入している場合、正確な検索結果は非決定論的であるため、このような同時ハッシュマップはまさに必要なものかもしれません。ただし、C++ で同時ハッシュ テーブルを使用したことはありませんが、Java で使用できるため、C++ でこれを行うライブラリを確実に見つけることができます。

于 2009-01-10T16:38:54.440 に答える