2

Win32 でのマルチスレッド アプリケーションという本を読んでいます。

この本はreturn node->next、アトミック操作として実行されない個別のマシン命令にコンパイルされるためNext()、クリティカルセクションによっても保護する必要があると述べています。

私の質問は、競合状態を引き起こすために、どのような命令に変換できるでしょうか?

typedef struct _Node
{
    struct Node *next;
    int data;
} Node;

typedef struct _List
{
    Node *head;
    CRITICAL SECTION critical_sec;
} List;

List *CreateList()
{
    List *pList = malloc(sizeof(List));
    pList->head = NULL;
    InitializeCriticalSection(&pList->critical_sec);
    return pList;
}

void DeleteList(List *pList)
{
    DeleteCriticalSection(&pList->critical_sec);
    free(pList);
}

void AddHead(List *pList, Node *node)
{
    EnterCriticalSection(&pList->critical_sec);
    node->next = pList->head;
    pList->head = node;
    LeaveCriticalSection(&pList->critical_sec);
}

void Insert(List *pList, Node *afterNode, Node *newNode)
{
    EnterCriticalSection(&pList->critical_sec);
    if (afterNode == NULL)
    {
        AddHead(pList, newNode);
    }
    else
    {
        newNode->next = afterNode->next;
        afterNode->next = newNode;
    }
    LeaveCriticalSection(&pList->critical_sec);
}

Node *Next(List *pList, Node *node)
{
    Node* next;
    EnterCriticalSection(&pList->critical_sec);
    next = node->next;
    LeaveCriticalSection(&pList->critical_sec);
    return next;
}

編集:

OK、この特定のケースでは、操作を保護せずに単一リンクリストを破損することはありませNext()んが、一般的に、共有構造は全体として保護するか、何も保護しないでください。

4

3 に答える 3

5

return node->next2 つの操作を実行します。最初structに byが指す をnodeメモリにロードし、次に を参照しnode+offsetof(next)て pointer を見つけ、nextそれをレジスタにロードしてから、呼び出しプログラムに戻ります。の内容はnode、実行中の別のスレッドによって操作される可能性があります。

于 2012-06-29T00:42:46.317 に答える
1
  1. はい、マルチスレッド アプリケーションでロックを使用して「次」を保護する必要があります。

... でも ...

  1. 「ライター」(ノードの追加または削除など)は相互に排他的でなければなりません。クリティカル セクションは適切な選択です。

  2. 「リーダー」(「次」など) は、互いに同時に実行できます。

提案:

Windows Vista 以降をターゲットにできる場合は、代わりに SRW ロックを使用することを検討してください。

于 2012-06-29T00:51:50.343 に答える
1

sarnold の回答は正しいと思いますが、CreateList の malloc() 呼び出しの sizeof() 呼び出しにバグがあるように見えることを指摘したかっただけです。私はそれがそうであるべきだと信じています:

List *pList = malloc(sizeof(List));

あなたが持っていた方法では、リスト構造ではなくリストへのポインタを保持するのに十分なメモリを作成します。(戻り値の型を (List*) にキャストし、それを使用する前に NULL と比較することもできます..)

于 2012-06-29T00:53:28.223 に答える