3

pThread ライブラリを使用したミューテックス ロックについて私が見たすべてのチュートリアルでは、グローバル ミューテックス ロックを使用しています。

見る:

https://computing.llnl.gov/tutorials/pthreads/#Mutex

http://www.drdobbs.com/cpp/184401518?pgno=3 (boost::thread の場合でも同じコンテキスト)

私がやりたいことは、メイン ファイルのグローバルが変数をロックする必要がある関数の範囲外にある Mutex ロックを使用することです。以下に例を示します。

メイン.cpp

int main() {
    some initilisation code.
    tree * tree1;
    *Start thread to visualise tree* (see code below)

    Mutex Lock:
         delete tree1;
         tree1 = newTree();
    Mutex Unlock

ビジュアライザー.cpp

visualise(Tree *) {
    Forever:
    Mutex Lock:
         Check for tree change.
         Update tree image.
         Display tree image.
    Mutex Unlock

これが可能かどうか知りたいです:

  1. グローバル スコープのミューテックス ロックなし。
  2. 可能であれば、ミューテックスを視覚化関数に渡さずに。

externを使用して、グローバルスコープ変数をvisualiser.cppに取得するにはどうすればよいでしょうか?

また、ミューテックスを関数に渡す必要がある場合、どうすればそれを行うことができますか?

4

3 に答える 3

5

はい、スレッドがミューテックスを使用している間、ミューテックスがスコープ内にとどまる場合は、グローバルである必要はありません。ミューテックスがどこにあるかを2番目のスレッドに伝える必要がありますが、残念ながらそれを回避する方法はありません。

そして、それを渡すことは、他の変数を渡すことと同じです。

したがって、単純に定義して最初のスレッドで初期化し、2番目のスレッドを作成するときに、そのアドレスをスレッド引数として渡します。

次に、2番目のスレッドはそのアドレスを使用してミューテックスにアクセスできます。


関数をスレッドとしても単純に通常の関数としても使用できるようにしたいというコメントに関しては、複雑さのためにそれを避けたいと思います。

あなたができることは、ほとんどの作業を通常の関数に入れてから、スレッド関数をその周りの単純なラッパーにすることです。有効な場合は使用できるミューテックスポインタを渡すことも、NULLの場合は使用しないミューテックスポインタを渡すこともできます。

詳細については、次の完全なプログラムを参照してください。まず、いくつかのサポート、必要なヘッダー、およびロギング機能:

#include <pthread.h>
#include <stdio.h>
#include <time.h>

static void mylog (int indent, char *s) {
    int i;
    time_t now = time (NULL);
    struct tm *lt = localtime (&now);
    printf ("%02d:%02d:%02d ", lt->tm_hour, lt->tm_min, lt->tm_sec);
    putchar ('|');
    for (i = 0; i < indent; i++) printf ("%-20s|", "");
    printf ("%-20s|", s);
    for (i = indent + 1; i < 3; i++) printf ("%-20s|", "");
    putchar ('\n');
}

次に、作業を行う関数。これは、任意のスレッドから呼び出すことができるように構築されており、ミューテックスポインターを使用する場合は、ミューテックスポインターを渡すことができます。

static void *myfunction (void *ptr) {
    pthread_mutex_t *pMtx = ptr;

    mylog (2, "starting");

    if (pMtx != NULL) {
        mylog (2, "locking mutex");
        pthread_mutex_lock (pMtx);
        mylog (2, "locked mutex");
    }

    mylog (2, "sleeping");
    sleep (5);
    mylog (2, "finished sleeping");

    if (pMtx != NULL) {
        mylog (2, "unlocking mutex");
        pthread_mutex_unlock (pMtx);
    }

    mylog (2, "stopping");
}

次に、実際のスレッド関数。これは、実際には上記の作業関数の薄いラッパーです。スレッド固有のパラメーターを介してミューテックスを受け取り、それを作業関数に渡すことに注意してください。

static void *mythread (void *ptr) {
    mylog (1, "starting");

    mylog (1, "call fn with mutex");
    myfunction (ptr);
    mylog (1, "and back");

    mylog (1, "stopping");
}

そして最後に、主な機能。これは、最初にミューテックスなしで作業関数を呼び出し、次に他のスレッドと共有するためのミューテックスを作成します。

int main (void) {
    pthread_mutex_t mtx;
    pthread_t tid1;
    char buff[100];

    printf ("         |%-20s|%-20s|%-20s|\n", "main", "thread", "workfn");
    printf ("         |%-20s|%-20s|%-20s|\n", "====", "======", "======");

    mylog (0, "starting");

    mylog (0, "call fn, no mutex");
    myfunction (NULL);
    mylog (0, "and back");

    mylog (0, "initing mutex");
    pthread_mutex_init (&mtx, NULL);

    mylog (0, "locking mutex");
    pthread_mutex_lock (&mtx);
    mylog (0, "locked mutex");

    mylog (0, "starting thead");
    pthread_create (&tid1, NULL, mythread, &mtx);

    mylog (0, "sleeping");
    sleep (5);
    mylog (0, "sleep done");

    mylog (0, "unlocking mutex");
    pthread_mutex_unlock (&mtx);

    mylog (0, "joining thread");
    pthread_join (tid1, NULL);
    mylog (0, "joined thread");

    mylog (0, "exiting");
    return 0;
}

出力で、コードがそれ自体をどのようにシーケンスするかを確認できます。

         |main                |thread              |workfn              |
         |====                |======              |======              |
15:07:10 |starting            |                    |                    |
15:07:10 |call fn, no mutex   |                    |                    |
15:07:10 |                    |                    |starting            |
15:07:10 |                    |                    |sleeping            |
15:07:15 |                    |                    |finished sleeping   |
15:07:15 |                    |                    |stopping            |
15:07:15 |and back            |                    |                    |
15:07:15 |initing mutex       |                    |                    |
15:07:15 |locking mutex       |                    |                    |
15:07:15 |locked mutex        |                    |                    |
15:07:15 |starting thead      |                    |                    |
15:07:15 |sleeping            |                    |                    |
15:07:15 |                    |starting            |                    |
15:07:15 |                    |call fn with mutex  |                    |
15:07:15 |                    |                    |starting            |
15:07:15 |                    |                    |locking mutex       |
15:07:20 |sleep done          |                    |                    |
15:07:20 |unlocking mutex     |                    |                    |
15:07:20 |joining thread      |                    |                    |
15:07:20 |                    |                    |locked mutex        |
15:07:20 |                    |                    |sleeping            |
15:07:25 |                    |                    |finished sleeping   |
15:07:25 |                    |                    |unlocking mutex     |
15:07:25 |                    |                    |stopping            |
15:07:25 |                    |and back            |                    |
15:07:25 |                    |stopping            |                    |
15:07:25 |joined thread       |                    |                    |
15:07:25 |exiting             |                    |                    |

特に、ミューテックスを使用した呼び出しと比較して、ミューテックスを使用しない直接呼び出しがどのように機能するかに注意してください。

于 2012-05-31T05:43:53.850 に答える
1

コードを疑似コードから具体的なコードに変換すると、答えが明らかになります。

例えば:

ビジュアライザー スレッドは、「ツリーの変更を確認する」必要があります。

これどうやってやるの?この情報を伝えることができるデータ構造が必要です。この ds はメイン スレッドによって更新されます。

したがって、ミューテックスをそのデータ構造内に保持します。グローバルまたはヒープ上にある可能性があります

于 2012-05-31T06:47:09.973 に答える
0

問題のすべてのスレッドに共通のオブジェクトへの複数のスレッドアクセスを同期しないため、スレッドごとに異なるミューテックスインスタンスを渡して使用することは、実際にはスレッドセーフではありません。

一般に、スレッドごとではなく、オブジェクト/データ構造インスタンスごとにミューテックスが必要です。そうは言っても、同期が必要なオブジェクトにミューテックス属性を持たせ、同期が必要なメソッドで内部的に管理する方が理にかなっています。

グローバルミューテックスの言及に関して、また前の段落を考慮すると、ミューテックスは、同期されるオブジェクトと関連するスレッドのコンテキストに対して効果的にグローバルである必要があります。つまり、同じミューテックスが共通であり、それらのすべてのエンティティで使用できる必要があります。前述のように、これはグローバル変数にせずに実現できます。

于 2012-05-31T06:55:46.780 に答える