2

私のアプリはメインプロセスと2つのスレッドで構成されており、すべて同時に実行され、3つのFIFOキューを使用しています。

fifo-qは、Qmain、Q1、およびQ2です。内部的には、キューはそれぞれ、アイテムがキューに入れられるとインクリメントされ、アイテムがキューから取得されるとデクリメントされるカウンターを使用します。

処理には
、Q1とQ2から取得してQmainに配置されるQMaster、
Q2に配置されるMonitor、
およびQmainから取得してQ1に配置されるメインプロセスの2つのスレッドが含まれます。

QMaster-threadループは、Q1とQ2のカウントを連続してチェックし、qにアイテムがある場合は、それらを取得してQmainに配置します。

Monitor-threadループは、外部ソースからデータを取得し、パッケージ化してQ2に配置します。

アプリのメインプロセスは、Qmainのカウントをチェックするループも実行し、アイテムがある場合は、ループの各反復でQmainからアイテムを取得し、さらに処理します。この処理中に、アイテムをQ1に入れて、後で処理することがあります(Qmainから順番に取得される場合)。

問題:
上記のようにすべてを実装しましたが、ランダムに(短い)時間動作してからハングします。私は、fifo-qのカウントのインクリメント/デクリメントで発生するクラッシュの原因を特定することができました(それらのいずれかで発生する可能性があります)。

私が試したこと
:QMAIN_LOCK、Q1_LOCK、Q2_LOCKの3つのミューテックスを使用します。これらは、関連するfifo-qでget/put操作が実行されるたびにロックします。結果:アプリが動作せず、ハングするだけです。

メインプロセスは常に実行を継続する必要があり、「読み取り」でブロックされてはなりません(名前付きパイプが失敗、ソケットペアが失敗)。

何かアドバイス?
ミューテックスを適切に実装していないと思いますが、どのようにすればよいですか?
(上記の設計の改善に関するコメントも歓迎します)

[編集]以下はプロセスとfifo-q-templateです:
上記の問題を回避するために、ミューテックスをどこにどのように配置する必要がありますか?

main-process:
...
start thread QMaster
start thread Monitor
...
while (!quit)
{
    ...
    if (Qmain.count() > 0)
    {
        X = Qmain.get();
        process(X) 
            delete X;
    }
    ...
    //at some random time:
    Q2.put(Y);
    ...
}

Monitor:
{
    while (1)
    {
        //obtain & package data
        Q2.put(data)
    }
}

QMaster:
{
    while(1)
    {
        if (Q1.count() > 0)
            Qmain.put(Q1.get());

        if (Q2.count() > 0)
            Qmain.put(Q2.get());
    }
}

fifo_q:
template < class X* > class fifo_q
{
    struct item
    {
        X* data;
        item *next;
        item() { data=NULL; next=NULL; }
    }
    item *head, *tail;
    int count;
public:
    fifo_q() { head=tail=NULL; count=0; }
    ~fifo_q() { clear(); /*deletes all items*/ }
    void put(X x) { item i=new item(); (... adds to tail...); count++; }
    X* get() { X *d = h.data; (...deletes head ...); count--; return d; }
    clear() {...}
};
4

6 に答える 6

1

すでに1つをロックしている場合は、2番目のミューテックスをロックしないでください。

質問はC++でタグ付けされているため、キュークラスのget / addロジック内にロックを実装するか(ブーストロックを使用するなど)、キューがクラスでない場合はラッパーを作成することをお勧めします。

これにより、ロックロジックを簡素化できます。

追加したソースに関して:キューサイズのチェックとそれに続くput / getは、1つのトランザクションで実行する必要があります。そうしないと、別のスレッドがその間のキューを編集できます。

于 2010-10-14T08:04:01.960 に答える
1

デバッガーを使用します。ミューテックスを使用したソリューションがハングした場合は、スレッドが何を行っているかを確認すると、問題の原因についての良いアイデアが得られます。

あなたのプラットフォームは何ですか?Unix / Linuxでは、POSIXメッセージキューを使用できるため(System Vメッセージキュー、ソケット、FIFOなども使用できます)、ミューテックスは必要ありません。

条件変数について学びます。あなたの説明によると、Qmasterスレッドはループに忙しく、CPUを焼き尽くしているようです。

あなたの回答の1つは、あなたが次のようなことをしていることを示唆しています。

Q2_mutex.lock()
Qmain_mutex.lock()
Qmain.put(Q2.get())
Qmain_mutex.unlock()
Q2_mutex.unlock()

しかし、あなたはおそらくそれを次のようにしたいと思うでしょう:

Q2_mutex.lock()
X = Q2.get()
Q2_mutex.unlock()

Qmain_mutex.lock()
Qmain.put(X)
Qmain_mutex.unlock()

グレゴリーが上で提案したように、ロジックをget/putにカプセル化します。

編集:あなたがあなたのコードを投稿したので、これは学習演習ですか?C++標準のstd::queueを使用する代わりに、独自のFIFOキュークラスをコーディングしていることがわかります。あなたはクラスを本当によくテストしていて、問題はそこにはないと思います。

また、なぜ3つの異なるキューが必要なのかわかりません。Qmainキューで十分なようです。そうすれば、実際に待機しているQmasterスレッドは必要ありません。

カプセル化については、fifo_qクラスをカプセル化するsynch_fifo_qクラスを作成できます。プライベートミューテックス変数を追加すると、パブリックメソッド(put、get、clear、count、...)はput(X){lockm_mutex;のようになります。m_fifo_q.put(X); m_mutexのロックを解除します。}

質問:キューに複数のリーダーがある場合はどうなりますか?「count()> 0」の後で、「get()」を実行して要素を取得できることが保証されていますか?

于 2010-10-14T08:36:25.297 に答える
1

私は以下に簡単なアプリケーションを書きました:

#include <queue>
#include <windows.h>
#include <process.h>
using namespace std;

queue<int> QMain, Q1, Q2;
CRITICAL_SECTION csMain, cs1, cs2;

unsigned  __stdcall TMaster(void*)
{
    while(1)
    {
        if( Q1.size() > 0)
        {
            ::EnterCriticalSection(&cs1);
            ::EnterCriticalSection(&csMain);
            int i1 = Q1.front();
            Q1.pop();
            //use i1;
            i1 = 2 * i1;
            //end use;
            QMain.push(i1);
            ::LeaveCriticalSection(&csMain);
            ::LeaveCriticalSection(&cs1);
        }
        if( Q2.size() > 0)
        {
            ::EnterCriticalSection(&cs2);
            ::EnterCriticalSection(&csMain);
            int i1 = Q2.front();
            Q2.pop();
            //use i1;
            i1 = 3 * i1;
            //end use;
            QMain.push(i1);
            ::LeaveCriticalSection(&csMain);
            ::LeaveCriticalSection(&cs2);
        }
    }
    return 0;
}

unsigned  __stdcall TMoniter(void*)
{
    while(1)
    {
        int irand = ::rand();
        if ( irand % 6 >= 3)
        {
            ::EnterCriticalSection(&cs2);
            Q2.push(irand % 6);
            ::LeaveCriticalSection(&cs2);
        }
    }
    return 0;
}

unsigned  __stdcall TMain(void)
{
    while(1)
    {
        if (QMain.size() > 0)
        {
            ::EnterCriticalSection(&cs1);
            ::EnterCriticalSection(&csMain);
            int i = QMain.front();
            QMain.pop();
            i = 4 * i;
            Q1.push(i);
            ::LeaveCriticalSection(&csMain);
            ::LeaveCriticalSection(&cs1);
        }
    }
    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    ::InitializeCriticalSection(&cs1);
    ::InitializeCriticalSection(&cs2);
    ::InitializeCriticalSection(&csMain);
    unsigned threadID;
    ::_beginthreadex(NULL, 0, &TMaster, NULL, 0, &threadID);
    ::_beginthreadex(NULL, 0, &TMoniter, NULL, 0, &threadID);
    TMain();

    return 0;
}
于 2010-10-14T10:00:48.593 に答える
1

デザインを適応させ、posixの方法でキューアクセスをロックする方法の例。ミューテックスをラップしてRAIIを使用するか、ブーストスレッドを使用し、stl::dequeまたはstl::queueをキューとして使用しますが、コードにできるだけ近づけてください。

main-process:
...
start thread Monitor
...
while (!quit)
{
    ...
    if (Qmain.count() > 0)
    {
        X = Qmain.get();
        process(X) 
            delete X;
    }
    ...
    //at some random time:
    QMain.put(Y);
    ...
}

Monitor:
{
    while (1)
    {
        //obtain & package data
        QMain.put(data)
    }
}

fifo_q:
template < class X* > class fifo_q
{
    struct item
    {
        X* data;
        item *next;
        item() { data=NULL; next=NULL; }
    }
    item *head, *tail;
    int count;
    pthread_mutex_t m;
public:
    fifo_q() { head=tail=NULL; count=0; }
    ~fifo_q() { clear(); /*deletes all items*/ }
    void put(X x) 
    { 
      pthread_mutex_lock(&m);
      item i=new item(); 
      (... adds to tail...); 
      count++; 
      pthread_mutex_unlock(&m);
    }
    X* get() 
    { 
      pthread_mutex_lock(&m);
      X *d = h.data; 
      (...deletes head ...); 
      count--; 
      pthread_mutex_unlock(&m);
      return d; 
    }
    clear() {...}
};

ここの例のようにミューテックスを初期化する必要があり、count()もミューテックスを使用する必要があることにも注意してください

于 2010-10-14T10:05:43.537 に答える
0

このルールにより、1つの問題が発生する可能性があります。「メインプロセスは常に実行を継続する必要があり、「読み取り」でブロックされてはなりません」。どのように実装しましたか?'get'と'read'の違いは何ですか?

問題は、ロジックではなく、実装にあるようです。そして、あなたが述べたように、あなたはロックにあるかどうかにかかわらず、別のロックを取得していないので、デッドロックに陥ってはなりません。

于 2010-10-14T08:24:09.657 に答える
0

複数のロックを同時に取得していますか?これは一般的に避けたいものです。必要な場合は、各スレッドで常に同じ順序でロックを取得していることを確認してください(これにより、同時実行性がより制限され、一般的にそれを避けたい理由がわかります)。

その他の同時実行のアドバイス:キューサイズを読み取る前にロックを取得していますか?キューを保護するためにミューテックスを使用している場合、キューの実装は並行していないため、キューのサイズを読み取る前にロックを取得する必要があります。

于 2010-10-14T07:49:15.130 に答える