我最近完成了一项作业,以实现一个线程安全的单链列表,该列表将元素锁定在交接方法中,这意味着列表中的每个元素都有一个锁,我实现了所有功能并测试了它们而不必处理有问题的线程情况,它们都可以工作,但是我想测试是否可能出现死锁或饥饿的情况,但是我不知道该怎么办,通过查看我的代码是否可以知道死锁或死锁?饥饿?我也不确定代码中是否需要这么多的锁定和解锁。 请注意,在每个函数中,我都锁定一个元素及其后继元素,这就足够了吗?如果我的代码容易出现死锁,饥饿或任何其他问题,有什么帮助或建议吗?我将在下面发布我的代码。预先感谢。
concurrent_list.h:
typedef struct node node;
typedef struct list list;
list* create_list();
void delete_list(list* list);
void print_list(list* list);
void insert_value(list* list, int value);
void remove_value(list* list, int value);
void count_list(list* list, int (*predicate)(int));
concurrent_list.c:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include "concurrent_list.h"
struct node {
int value;
node* next;
pthread_mutex_t lock; // hand over hand implies a lock for every node
};
struct list {
node* head;
};
// print the value of a node
void print_node(node* node)
{
if(node)
{
printf("%d ", node->value);
}
}
// create a new empty list
list* create_list()
{
list* l =(list*)malloc(sizeof(list));
if(l == NULL) return NULL;
l->head = NULL;
return l;
}
// delete the entire list
void delete_list(list* list)
{
if(list == NULL) return; // if list pointer is NULL then do nothing
node* current = list->head;
if(current == NULL) return; // if list head is NULL then do nothing
pthread_mutex_lock(&(current->lock)); // lock list head
node* temp = current->next;
node* dummy;
if(temp == NULL) // delete the only element in the list
{
pthread_mutex_unlock(&(current->lock));
pthread_mutex_destroy(&(current->lock));
free(current);
return;
}
pthread_mutex_lock(&(temp->lock)); //lock successor of the head
while (1)
{
pthread_mutex_unlock(&(current->lock)); // unlock current node
dummy = current;
current = temp; // current becomes it's successor
pthread_mutex_destroy(&(dummy->lock));
free(dummy); // free dummy because it is a pointer to current
temp = temp->next; // current becomes it's successor
if(temp == NULL) break; // exit loop if we are at the end of the
list
pthread_mutex_lock(&(temp->lock)); // lock current's successor
}
pthread_mutex_unlock(&(current->lock));
pthread_mutex_destroy(&(current->lock));
free(current); // free the last element in the list
list->head = NULL;
free(list); // free the list
}
// insert function for a new value if a value already exists then do
nothing
void insert_value(list* list, int value)
{
if(list == NULL) return; // if list pointer is NULL then do nothing
node* new_node = malloc(sizeof(node)); // create new node
if(new_node == NULL) return; // check if allocation fails
new_node->value = value;
new_node->next = NULL;
pthread_mutex_init(&(new_node->lock),NULL); // initialize fast mutex lock for the new node
pthread_mutex_lock(&(new_node->lock)); // lock the new node
if(list->head == NULL) // new node is the first element in the list
{
new_node->next = NULL;
list->head = new_node;
pthread_mutex_unlock(&(new_node->lock));
return;
}
pthread_mutex_lock(&(list->head->lock)); // lock the head of the list
node* temp;
if(list->head->value >= new_node->value) // new node comes before the list head
{
new_node->next = list->head;
temp = list->head;
list->head = new_node;
pthread_mutex_unlock(&(list->head->lock));
pthread_mutex_unlock(&(temp->lock));
return;
}
else
{
// Locate the node before the point of insertion //
node* dummy;
node* current = list->head;
temp = current->next;
if(temp == NULL) // new node comes after the list head
{
new_node->next = current->next;
current->next = new_node;
pthread_mutex_unlock(&(new_node->lock));
pthread_mutex_unlock(&(current->lock));
return;
}
pthread_mutex_lock(&(temp->lock)); // lock the successor of the head
// perform hand over hand traversal of the list
// and check if temp reaches the end of the list (NULL)
while (temp->value < new_node->value)
{
pthread_mutex_unlock(&(current->lock));
current = temp;
temp = temp->next;
if(temp == NULL) break;
pthread_mutex_lock(&(temp->lock));
}
if(temp == NULL) // new node will be the last element in this case
{
current->next = new_node;
new_node->next = NULL;
pthread_mutex_unlock(&(current->lock));
pthread_mutex_unlock(&(new_node->lock));
return;
}
else // new node should be inserted inside the list
{
dummy = temp;
new_node->next = current->next;
current->next = new_node;
pthread_mutex_unlock(&(dummy->lock));
pthread_mutex_unlock(&(current->lock));
pthread_mutex_unlock(&(new_node->lock));
return;
}
}
}
//delete the first appearance of a value in the list if it exists in the list
void remove_value(list* list, int value)
{
if(list == NULL) return; // if list pointer is NULL then do nothing
node* temp;
node* current = list->head;
if(current == NULL) return; // if list head is NULL then just go to a new line
pthread_mutex_lock(&(current->lock)); // lock the head of the list
if(current->value == value) // delete the head of list if it's value is equal to given value
{
temp = current;
list->head = current->next;
pthread_mutex_unlock(&(temp->lock));
pthread_mutex_destroy(&(temp->lock));
free(temp);
return;
}
else
{
temp = current->next;
if(temp == NULL)
{
pthread_mutex_unlock(&(current->lock));
return;
}
pthread_mutex_lock(&(temp->lock)); // lock the successor of the head
// perform hand over hand traversal of the list
// and check if temp reaches the end of the list (NULL)
while (temp->value != value) // find the first appearance of a node
that has a value the same as the one given
{
pthread_mutex_unlock(&(current->lock));
current = temp;
temp = temp->next;
if(temp == NULL) break;
pthread_mutex_lock(&(temp->lock));
}
if(temp == NULL) // value not found
{
pthread_mutex_unlock(&(current->lock));
return;
}
else // delete the suitable node
{
current->next = temp->next;
pthread_mutex_unlock(&(current->lock));
pthread_mutex_unlock(&(temp->lock));
pthread_mutex_destroy(&(temp->lock));
free(temp);
return;
}
}
}
//print the entire list
void print_list(list* list)
{
if(list == NULL) // if list pointer is NULL then just go to a new line
{
printf("\n");
return;
}
node* current = list->head;
if(current == NULL) // if list head is NULL then just go to a new line
{
printf("\n");
return;
}
pthread_mutex_lock(&(current->lock)); // lock the head of the list
print_node(current); // print the head of the list
node* temp = current->next;
if(temp == NULL) // a list with 1 element
{
printf("\n");
pthread_mutex_unlock(&(current->lock));
return;
}
pthread_mutex_lock(&(temp->lock)); // lock the list head's successor
while (1)
{
pthread_mutex_unlock(&(current->lock)); // unlock current node
current = temp; // current becomes it's successor
print_node(current); // print current node
temp = temp->next; // // temp becomes it's successor
if(temp == NULL) break; // exit loop if we reach the end of the list
pthread_mutex_lock(&(temp->lock)); // lock temp
}
pthread_mutex_unlock(&(current->lock)); // unlock the last list
element
printf("\n");
}
// print how many nodes in the list satisfy a given predicate function
void count_list(list* list, int (*predicate)(int))
{
int count = 0;
if(list == NULL) // if list pointer is NULL then print that count =
0 and finish
{
printf("%d items were counted\n", count);
return;
}
node* current = list->head;
if(current == NULL) // if list head is NULL then print that count =
0 and finish
{
printf("%d items were counted\n", count);
return;
}
pthread_mutex_lock(&(current->lock)); // lock the list head
if(predicate(current->value)) count++; // check the predicate for
the list head
node* temp = current->next;
if(temp == NULL) // list has only 1 element
{
pthread_mutex_unlock(&(current->lock));
printf("%d items were counted\n", count);
return;
}
pthread_mutex_lock(&(temp->lock)); // lock the list head's successor
while (1)
{
pthread_mutex_unlock(&(current->lock)); // unlock current node
current = temp; // current becomes it's successor
if(predicate(current->value)) count++; // check predicate for current node
temp = temp->next; // // temp becomes it's successor
if(temp == NULL) break; // exit loop if we reach the end of the list
pthread_mutex_lock(&(temp->lock)); // lock temp
}
pthread_mutex_unlock(&(current->lock)); // unlock the last list element
printf("%d items were counted\n", count); // print count
}
答案 0 :(得分:0)
始终存在该列表的用户代码被死锁(自身或与其他用户代码互锁)的风险。您无法避免这种情况。
在考虑列表代码本身是否可能出现死锁之前,必须对代码进行纠正:
列表本身未锁定。无法安全地完成head
的替换。
在delete_list
中:
我没有继续,但是猜测其余的代码中也存在类似的问题。这些问题应在发现僵局之前得到解决。
答案 1 :(得分:0)
我认为您使问题复杂化了,结果,在某些情况下您仍然会进入并发更新列表。
例如,在您的insert_value
中,在锁定列表标题之前,您有以下几行内容:
node* current = list->head;
temp = current->next;
如果其他线程试图同时删除头部,则可能会导致问题。 可能还有其他人。
在许多情况下,您将new
值锁定在不需要的地方,因为该值是由线程创建的,尚未共享。
通常,如果对列表执行操作,则对整个列表使用单个互斥锁。如果允许同时修改元素,则可能需要锁定元素。
我想在您的情况下,仅锁定head元素就足以完成大多数操作。