我想在多个线程中修改数组(或结构)的一些(不是全部)字段,而不阻塞数组的其余部分,因为它的其余部分正在其他线程中修改。这是如何实现的?我找到了一些答案,但它们是针对 C++ 的,我想用 C 来做。 这是我到目前为止得到的代码:
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#define ARRAYLENGTH 5
#define TARGET 10000
int target;
typedef struct zstr{
int* array;
int place;
int run;
pthread_mutex_t* locks;
}zstr;
void *countup(void *);
int main(int argc, char** args){
int al;
if(argc>2){
al=atoi(args[1]);
target=atoi(args[2]);
}else{
al=ARRAYLENGTH;
target=TARGET;
}
printf("%d %d\n", al, target);
zstr* t=malloc(sizeof(zstr));
t->array=calloc(al, sizeof(int));
t->locks=calloc(al, sizeof(pthread_mutex_t));
int* rua=calloc(al, sizeof(int));
pthread_t id[4*al];
for(int i=0; i<al; i++)
pthread_mutex_init(&(t->locks[i]), NULL);
for(int j=0; j<4*al; j++){
int st=j%al;
t->run=rua[st]++;
t->place=st;
pthread_create(&id[j], NULL, &countup, t);
}
for(int k=0; k<4*al; k++){
pthread_join(id[k], NULL);
}
for(int u=0; u<al; u++)
printf("%d\n", t->array[u]);
free(rua);
free(t->locks);
free(t->array);
return 0;
}
void *countup(void* table){
zstr* nu=table;
if(!nu->run){
pthread_mutex_lock(nu->locks + nu->place);
}else{
pthread_mutex_trylock(nu->locks + nu->place);
}
while(nu->array[nu->place]<target)
nu->array[nu->place]++;
pthread_mutex_unlock(nu->locks + nu->place);
return NULL;
}
有时这工作得很好,但随后计算出错误的值,并且对于安静的排序问题(例如默认值),它需要很长时间(奇怪的是,当我将它们作为参数传递时,它工作了一次)。
最佳答案
数组或结构的一部分没有什么特别的。重要的是正确使用应用于给定值的互斥体或其他同步。
在这种情况下,您似乎没有检查锁定函数的结果。
countup 函数的设计只允许单个线程访问该对象,在释放锁之前将值一直运行到目标,但不检查 trylock 结果。
因此,可能发生的情况是第一个线程获得锁,同一互斥锁上的后续线程调用 trylock 并未能获得锁,但代码不检查结果。然后,多个线程会在不同步的情况下递增相同的值。鉴于所有指针取消引用,索引和增量操作不能保证是原子的,从而导致值增长远远超出目标的问题。
这个故事的寓意是检查函数结果并处理错误。
关于在纯c中,在线程中更改数组/结构/..的部分而不阻塞整个事情,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37557292/