在线程安全链接列表中检测死锁

时间:2019-06-21 14:12:03

标签: c linked-list thread-safety deadlock

我最近完成了一项作业,以实现一个线程安全的单链列表,该列表将元素锁定在交接方法中,这意味着列表中的每个元素都有一个锁,我实现了所有功能并测试了它们而不必处理有问题的线程情况,它们都可以工作,但是我想测试是否可能出现死锁或饥饿的情况,但是我不知道该怎么办,通过查看我的代码是否可以知道死锁或死锁?饥饿?我也不确定代码中是否需要这么多的锁定和解锁。 请注意,在每个函数中,我都锁定一个元素及其后继元素,这就足够了吗?如果我的代码容易出现死锁,饥饿或任何其他问题,有什么帮助或建议吗?我将在下面发布我的代码。预先感谢。

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
}

2 个答案:

答案 0 :(得分:0)

始终存在该列表的用户代码被死锁(自身或与其他用户代码互锁)的风险。您无法避免这种情况。

在考虑列表代码本身是否可能出现死锁之前,必须对代码进行纠正:

列表本身未锁定。无法安全地完成head的替换。

delete_list中:

    在单项列表(temp == NULL)的情况下,
  • :先解锁然后释放:这是使锁定无效的种族。另外,也不必解锁,因为该内存将被释放。
  • 对于列表包含更多元素的情况类似,但是您还可以在解锁后找出后继者(请注意,其他一些线程可能会看到解锁并更改后继者指针,然后第一个线程继续以错误的方式运行后继者)

我没有继续,但是猜测其余的代码中也存在类似的问题。这些问题应在发现僵局之前得到解决。

答案 1 :(得分:0)

我认为您使问题复杂化了,结果,在某些情况下您仍然会进入并发更新列表。

例如,在您的insert_value中,在锁定列表标题之前,您有以下几行内容:

 node* current = list->head;
 temp = current->next;

如果其他线程试图同时删除头部,则可能会导致问题。 可能还有其他人。

在许多情况下,您将new值锁定在不需要的地方,因为该值是由线程创建的,尚未共享。

通常,如果对列表执行操作,则对整个列表使用单个互斥锁。如果允许同时修改元素,则可能需要锁定元素。

我想在您的情况下,仅锁定head元素就足以完成大多数操作。