简体   繁体   English

为什么单链表的Next()操作应由关键部分保护?

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

I'm reading the book Multithreading Applications in Win32 我正在阅读Win32中的《 多线程应用程序 》一书

The book says return node->next will be compiled into separate machine instructions that would not be executed as an atomic operation, so Next() should also be protected by the critical section. 该书说, return node->next将被编译成单独的机器指令,这些指令不会作为原子操作执行,因此Next()也应由关键部分保护。

My question is, what instructions could it be translated into, to cause a race condition? 我的问题是,它会被翻译成什么指令以引起比赛状况?

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;
}

Edit: 编辑:

OK, although in this particular case it won't corrupt the singly linked list w/o protecting the Next() operation, a shared structure should be protected as a whole or nothing, generally. 好的,尽管在这种特殊情况下它不会破坏单个链表而不保护Next()操作,但是共享结构应该整体上受到保护,或者一无所获。

return node->next performs two operations; return node->next执行两项操作; it first loads the struct pointed to by node into memory, then looks at the node+offsetof(next) to find the pointer next , load that into a register, and then return to the calling program. 它首先将node指向的struct加载到内存中,然后查看node+offsetof(next)以查找next指针,将其加载到寄存器中,然后返回到调用程序。 The contents of node may be manipulated by another thread of execution in the meantime. 同时, node的内容可能会被另一个执行线程操纵。

  1. Yes, you absolutely need to protect your "next" with a lock in a multithreaded application. 是的,您绝对需要在多线程应用程序中使用锁来保护“下一个”。

... HOWEVER ... ...但是...

  1. "Writers" (like add or remove node) MUST be mutually exclusive. “作家”(如添加或删除节点) 必须互斥。 Critical section is a good choice. 关键部分是一个不错的选择。

  2. "Readers" (like "next") can run concurrently with each other. “读者”(例如“下一个”)可以彼此并发运行。

SUGGESTION: 建议:

If you can target Windows Vista or higher, consider using an SRW lock instead: 如果可以使用Windows Vista或更高版本,请考虑使用SRW锁:

While I think the answer by sarnold is correct, I just wanted to point out that the sizeof() call in your malloc() call in CreateList appears to have a bug. 虽然我认为sarnold的答案是正确的,但我只是想指出,CreateList中malloc()调用中的sizeof()调用似乎有一个错误。 I believe it should be: 我相信它应该是:

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

The way you had it, you will create enough memory to hold a pointer to a List and not the List structure. 按照这种方式,您将创建足够的内存来保存指向 List而不是List结构的指针 (You may also want to cast the return type to (List*) and compare it to NULL before using it..) (您可能还希望将返回类型强制转换为(List *)并将其与NULL比较,然后再使用。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM