指针变量周围是否需要互斥锁?

时间:2011-04-10 15:54:03

标签: c linux multithreading pointers mutex

是否需要在涉及指针间接的代码段周围使用互斥锁(其中指针指向属于临界区的数据)?示例代码:

struct list {
    int i;
    struct list *next;
};

int modify_second_elem(struct list *head, int val);
void * func1(void *ptr);
void * func2(void *ptr);

int modify_second_elem(struct list *head, int val) {

    if(head == NULL)
        return 1;

    /* Check to see if second element exists.
       Here, I am using indirection to get the next pointer.
       Would I need synchronization here? */
    if(head->next == NULL)
        return -1;

    pthread_mutex_lock(&list_lock);
    (head->next)->i = val;
    pthread_mutex_unlock(&list_lock);

    return 0;

}

void * func1(void *ptr) {
    struct list *head;
    head = (struct list *) ptr;
    modify_second_elem(head, 4);
}

void * func2(void *ptr) {
    struct list *head;
    head = (struct list *) ptr;
    modify_second_elem(head, 6);
}

void main() {
    struct list *el1, *el2, *el3;
    pthread_t th1, th2;

    el1 = (struct list *) malloc(sizeof(list));
    el2 = (struct list *) malloc(sizeof(list));
    el3 = (struct list *) malloc(sizeof(list));

    el1->i = 1;
    el1->next = el2;
    el2->i = 2;
    el2->next = el3;
    el3->i = 3;
    el3->next = NULL;

    pthread_create(&th1, NULL, &func1, (void *) el1);
    pthread_create(&th2, NULL, &func2, (void *) el1);

    pthread_join(th1, NULL);
    pthread_join(th2, NULL);

    exit(EXIT_SUCCESS);
}

4 个答案:

答案 0 :(得分:4)

锁用于保护代码中的critical section。如果您的变量处于临界区,那么您需要使用某种锁来保护它。

答案 1 :(得分:4)

没有足够的信息给出一个非常好的答案。 s如何“发布”到其他线程?其他线程如何“订阅”s?在哪个数据结构中存储了struct s个对象?

所以,我会给出一个通用答案:

线程之间共享的每种数据都需要同步。这包括共享指针。

关于指针:

您可能听说过,在某些普通的CPU上,正确对齐的指针的加载/存储是原子的(对于所有CPU或所有类型的指针都不是这种情况,例如:x86上的远指针是非原子的)。如果你没有彻底了解你的CPU / VM内存模型,请远离使用它,如果你不采取锁定(锁提供相当强的保证),有许多微妙的事情可能会出错。

编辑:

在您的示例中,th1th2都不会修改列表,它们只会修改列表的元素。因此,在这种特定情况下,您不需要锁定列表,只需要锁定列表的元素(指针在概念上属于链表实现)。

在更典型的情况下,一些线程将遍历列表,而其他线程将修改列表(添加和删除元素)。这需要锁定列表,或使用某种无锁算法。

执行此锁定有多种方法。

  • 列表中包含全局锁定,并将其用于列表中的元素。
  • 使用分层锁定,锁定列表,并锁定每个元素。要读取/修改元素,首先要锁定列表,找到元素,获取元素的锁定,释放列表的锁定,处理元素,最后释放元素的锁定。如果您需要对元素执行一些复杂的处理并且不希望阻止其他线程访问列表,这将非常有用。 您必须注意始终以相同的顺序获取锁,以避免死锁。

答案 2 :(得分:2)

锁用于保护critical sections。它与作为指针的变量无关。

在您的情况下,您不需要锁定该检查。由于s是参数,因此无法从函数外部访问它。但是,它指向的数据不是您的函数的本地数据,因此您可能需要锁定对该数据的访问权。

答案 3 :(得分:2)

这取决于您对s的其他操作。例如,另一个修改s->c值的线程取决于s->i

if (s->i == 1) {
    s->c = 'x';
} else {
    s->c = 'y';
}

在这种情况下,您无法省略互斥锁,否则您可能同时将s->i设置为1,将s->c设置为“y”。