是否需要在涉及指针间接的代码段周围使用互斥锁(其中指针指向属于临界区的数据)?示例代码:
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);
}
答案 0 :(得分:4)
锁用于保护代码中的critical section。如果您的变量处于临界区,那么您需要使用某种锁来保护它。
答案 1 :(得分:4)
没有足够的信息给出一个非常好的答案。 s
如何“发布”到其他线程?其他线程如何“订阅”s
?在哪个数据结构中存储了struct s
个对象?
所以,我会给出一个通用答案:
线程之间共享的每种数据都需要同步。这包括共享指针。
关于指针:
您可能听说过,在某些普通的CPU上,正确对齐的指针的加载/存储是原子的(对于所有CPU或所有类型的指针都不是这种情况,例如:x86上的远指针是非原子的)。如果你没有彻底了解你的CPU / VM内存模型,请远离使用它,如果你不采取锁定(锁提供相当强的保证),有许多微妙的事情可能会出错。
在您的示例中,th1
和th2
都不会修改列表,它们只会修改列表的元素。因此,在这种特定情况下,您不需要锁定列表,只需要锁定列表的元素(指针在概念上属于链表实现)。
在更典型的情况下,一些线程将遍历列表,而其他线程将修改列表(添加和删除元素)。这需要锁定列表,或使用某种无锁算法。
执行此锁定有多种方法。
答案 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”。