8

以前に Python でマルチスレッド ライブラリを使用したことがありますが、C でスレッド化を試みるのはこれが初めてです。ワーカーのプールを作成したいと考えています。次に、これらのワーカーは、キューにプッシュまたはキューからポップすることになっています。次のコードはまだ完成していませんが、これまでに行ったことです。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define NUMTHREADS 20 /* number of threads to create */

typedef struct node node;
typedef struct queue queue;

struct node {
    char *name;
    node *next;
};

struct queue {
    node *head;
    node *tail;
};

/* pop: remove and return first name from a queue */
char *pop(queue *q)
{
    if (q->head == NULL)
        return NULL;
    char *name = q->head->name;
    node *tmp = q->head;
    q->head = q->head->next;
    free(tmp);
    return name;
}

/* push: add name to the end of the queue */
int push(queue *q, char *name)
{
    node *new = malloc(sizeof(node));
    if (new == NULL)
        return -1;
    new->name = name;
    new->next = NULL;
    if (q->tail != NULL)
        q->tail->next = new;

    q->tail = new;
    if (q->head == NULL) /* first value */
        q->head = new;
    return 0;
}

/* printname: get a name from the queue, and print it. */
void *printname(void *sharedQ)
{
    queue *q = (queue *) sharedQ;
    char *name = pop(q);
    if (name == NULL)
        pthread_exit(NULL);
    printf("%s\n",name);
    pthread_exit(NULL);
}

int main()
{
    size_t i;
    int rc;
    pthread_t threads[NUMTHREADS];
    char *names[] = {
        "yasar",
        "arabaci",
        "osman",
        "ahmet",
        "mehmet",
        "zeliha"
    };

    queue *q = malloc(sizeof(queue));
    q->head = NULL;
    q->tail = NULL;

    /* number of elements in the array */
    size_t numelems = sizeof(names) / sizeof(char *);

    for (i = 0; i < numelems; i++) /* push each name */
        push(q, names[i]);

    for (i = 0; i < NUMTHREADS; i++) { /* fire up threads */
        rc = pthread_create(&threads[i], NULL, printname,
                (void *)q);
        if (rc) {
            printf("Error, return code from pthread is %d\n", rc);
            exit(-1);
        }
    }

    pthread_exit(NULL);
}

上記のコードを試してみましたが、常に各名前が一度だけ出力されました。名前をスキップしたり、同じ名前を 2 回出力したりしませんでした。一方で、このキューの実装がどれほどスレッドセーフかはわかりません。だから私の質問は、これはスレッドセーフなキューですか? そうでない場合、なぜですか?そして、それをスレッドセーフにする方法は?

4

2 に答える 2

7

コードはスレッドセーフではありません。

プッシュおよびポップ関数はスレッドセーフではありません。コードでは、push は 1 つのスレッドで実行されているだけなので問題ありませんが、pops は複数のスレッドで実行されています。

1. char *name = q->head->name;
2. node *tmp = q->head;
3. q->head = q->head->next;
4. free(tmp);

スレッド A が 2 行目まで実行され、スレッド B が 4 行目まで実行されたとします。スレッド A は実行を再開します。q->head がすでに free() されていることがわかります。

さて、これまでのところ、論理的な問題について説明しています。

ただし、考慮すべき物理的な問題があります。

一度に 1 つのスレッドだけが行 1 から 4 のコードを実行できるように、スレッドが動作を同期できるロック メカニズムがあると想像してください。 、およびミューテックスを取得しようとすると、保持しているスレッドが解放されるまでスレッドがブロックされます。

0. get mutex
1. char *name = q->head->name;
2. node *tmp = q->head;
3. q->head = q->head->next;
4. free(tmp);
5. release mutex

特定の CPU コア (スレッドではない) によって実行された書き込みは、そのコアのスレッドにのみすぐに見えるという点で、まだ問題があります。他のコアのスレッドではありません。

実行を同期させるだけでは十分ではありません。同時に、コアによって実行された書き込みが他のコアから見えるようにする必要もあります。

(残念ながら) 残念なことに、最新のすべての同期方法もこの書き込みフラッシュを実行します (たとえば、mutex を取得すると、すべての書き込みもメモリにフラッシュします)。残念ながら、この動作は必ずしも必要ではなく、パフォーマンスに悪影響を与えるためです。

于 2012-05-23T13:58:39.867 に答える
3

複数のスレッドがリンクされたリスト内のポインターを同時に変更し、破壊する可能性があるため、スレッドセーフではありません。

ここに、非常によく似た質問に対する答えがあります: C の複数ライター スレッドセーフ キュー

そこで、キューをスレッドセーフにする方法を確認できます。

于 2012-05-23T14:00:38.697 に答える