Краткое резюме: Вы сохраняете адрес локальной переменной в узле списка, и эта переменная выходит из области видимости, поэтому адрес становится недействительным.
Ваша функция new_node(void *val)
получает адрес в качестве аргумента и сохраняет этот адрес в структуре узла как node->val
.
В вашей функции main
вы сначала создаете 3 узла с адресами локальных переменных.
int l, a=2, b=8, c=15;
l=insert_first(list[0], &a);
l=insert_end(list[0], &b);
l=insert_mid(list[0], &c, rand()%l);
Эти переменные действительны до конца main
, поэтому здесь нет проблем.
В этом l oop
for(thread=0;thread<thread_count;thread++)
pthread_create(&thread_handles[thread], NULL, thread_operation, (void *)thread);
вы создаете потоки, работающие thread_operation
и передать счетчик l oop, приведенный к void*
в качестве аргумента.
В thread_operation
при вызове со значением аргумента 0 вы добавляете узел, используя адрес локальной переменной v
void *thread_operation(void * arg)
{
long id= (long) arg;
if(id==0)
{
/* ... */
int v=rand()%100;
int pos=rand()%list[0]->len;
/* ... */
pos=insert_mid(list[0], &v, pos); /* &v is the address of a local variable */
/* ... */
}
else
{
/* ... */
}
}
/* ... */
int insert_mid(ll_t list, void *val, int n)
{
ll_node node= new_node(val); /* The address is passed to the new node. */
/* ... */
}
Когда thread_operation
покидает тело
if(id==0)
{
/* ... */
}
, переменная выходит из области видимости и становится недействительной. (Если вы компилируете свою программу без оптимизации, она, вероятно, сохраняет свое значение до тех пор, пока функция не вернется, но это не гарантируется.)
Когда вернется thread_operation
, область стека, в которой находилась эта переменная, будет расположена и будет используется для других вызовов функций позже.
Когда вы печатаете содержимое списка, узел, вставленный потоком с идентификатором 0, указывает на некоторый адрес в стеке, который когда-то содержал переменную v
и мог находиться в использовать или могли быть использованы для чего-то еще впоследствии. Вот почему значение меняется. Это неопределенное поведение.
Чтобы решить эту проблему, вы можете либо изменить структуру вашего узла на , сохранить значение переменной в виде int
вместо адреса переменной как void *
или Вы должны выделить память и создать копию данных следующим образом
ll_node new_node(void *val, size_t size)
{
ll_node node= (ll_node)malloc(sizeof(struct node));
/* TODO check that malloc did not return NULL */
node->val = malloc(size);
/* TODO check that malloc did not return NULL */
memcpy(node->val, val, size);
node->next=NULL;
pthread_rwlock_init(&node->m, NULL);
return node;
}
Дополнительная возможная проблема:
Вы создаете объект блокировки в каждом узле списка. Если вы хотите использовать это для защиты доступа к данным в узле, соответствующий поток должен получить (как минимум) блокировку чтения для списка, чтобы другие потоки не могли удалить узел.