为什么单向链表的 Next() 操作应该用关键部分来保护

Why should Next() operation of a singly linked list be protected with a critical section?

本文关键字:键部 保护 操作 链表 Next 为什么      更新时间:2023-10-16

>我正在阅读《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;
}

编辑:

好的,尽管在这种特殊情况下,它不会破坏不保护Next()操作的单向链表,但通常应该保护整个共享结构或什么都不保护。

return node->next执行两个操作;它首先将node指向的struct加载到内存中,然后查看node+offsetof(next)以查找指针next,将其加载到寄存器中,然后返回到调用程序。在此期间,node的内容可能会被另一个执行线程操作。

  1. 是的,您绝对需要在多线程应用程序中使用锁定来保护您的"下一个"。

。然而。。。

  1. "编写器"(如添加或删除节点)必须是互斥的。 关键部分是一个不错的选择。

  2. "
  3. 读取器"(如"下一个")可以彼此同时运行。

建议:

如果可以面向 Windows Vista 或更高版本,请考虑改用 SRW 锁:

  • http://msdn.microsoft.com/en-us/library/aa904937%28VS.85%29.aspx

  • http://msdn.microsoft.com/en-us/magazine/cc163405.aspx

虽然我认为 sarnold 的答案是正确的,但我只是想指出 CreateList 中 malloc() 调用中的 sizeof() 调用似乎有一个错误。 我认为应该是:

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

按照你拥有它的方式,你将创建足够的内存来保存指向列表而不是列表结构的指针。 (您可能还希望将返回类型强制转换为 (List*),并在使用它之前将其与 NULL 进行比较。