在C套接字编程中使用链表进行线程安全

时间:2015-10-14 23:52:54

标签: multithreading sockets linked-list thread-safety pthreads

我正在使用线程和TCP在C中实现P2P文件传输。我有一个跟踪器,它跟踪连接到它的所有对等端。当对等体首次连接时,它将其文件从其当前目录发送到将所有对等体的文件放在共享链接列表中的跟踪器。每个对等体在跟踪器中都有自己的线程,当对等体退出时,跟踪器关闭该对等体的连接,并从链接列表中删除该对等体中的所有文件。我关心的是在添加和删除节点时锁定链表。我希望能够安全地从各种线程更改链表。我已经编写了我的代码来添加/删除我的链表并搜索(不确定我是否需要在搜索时使用锁定)如何修改我的代码以确保线程之间的链接安全?

2 个答案:

答案 0 :(得分:1)

请考虑使用互斥锁来保护数据或其他资源免遭并发访问。

在POSIX线程的上下文中,pthread_mutex_t类型(来自sys/types.h, The Open Group Base Specifications Issue 7, IEEE Std 1003.1, 2013 Edition)用于互斥锁。

pthread_mutex_lock()pthread_mutex_unlock()函数(The Open Group Base Specifications Issue 7, IEEE Std 1003.1, 2013 Edition)可用于保护链接列表类型的实例免受两个操作的并发访问:

  • 读取操作:枚举列表(即读取next指针)和使用(读/写)存储在节点中的数据时;
  • 写操作:更改节点:next指针和存储在节点中的数据。

此外,可以使用相同的互斥锁来保护对[{1}} [链接列表]指针的访问。

示例:

head

值得注意的是,减少锁争用 - 性能调优并发应用程序的重要部分。

与套接字相关的问题

// ... pthread_mutex_t list_mutex = PTHREAD_MUTEX_INITIALIZER; // ... void *peer_handler(void *p) { // ... pthread_mutex_lock(&list_mutex); if (numNodes == 0) { head = newNode; tail = newNode; tail->next = NULL; numNodes++; } else { tail->next = newNode; tail = newNode; tail->next = NULL; numNodes++; } pthread_mutex_unlock(&list_mutex); // ... pthread_mutex_lock(&list_mutex); struct fileNode *ptr = head; while (ptr != NULL) { if ((strcmp(ptr->ip, temp_ip) == 0) && ptr->port == temp_port) { cmd = htonl(200); break; } ptr = ptr->next; } pthread_mutex_unlock(&list_mutex); // ... } void sendList(int newsockfd) { // ... pthread_mutex_lock(&list_mutex); struct fileNode *ptr; ptr = head; while (ptr != NULL) { // ... ptr = ptr->next; // ... } pthread_mutex_unlock(&list_mutex); } void removeFiles(uint32_t port, char *client_ip) { pthread_mutex_lock(&list_mutex); // ... pthread_mutex_unlock(&list_mutex); } void print() { // ... pthread_mutex_lock(&list_mutex); struct fileNode *ptr; ptr = head; while (ptr != NULL) { // ... ptr = ptr->next; // ... } pthread_mutex_unlock(&list_mutex); } send()函数不保证一次函数调用将发送/接收所有指定的字节数

应使用适当的循环发送或接收所需(预期)字节数。有关其他详细信息,请参阅文章:TCP/IP client-server application: exchange with string messages

另外,请注意这些结构:

recv()

要接收的预期字节数(或者,在本例中为字符)应为n = recv(newsockfd, buffer, sizeof(buffer), 0); // ... buffer[n] = '\0'; 。否则,数据丢失:最后一个字符被终止空字符覆盖。

答案 1 :(得分:0)

最简单的解决方案是在访问列表之前锁定一个pthread_mutex_t,并在完成后解锁。将它与列表变量一起声明:

struct fileNode *head, *tail;
int numNodes = 0;
pthread_mutex_t list_lock = PTHREAD_MUTEX_INITIALIZER;

例如。添加节点时:

pthread_mutex_lock(&list_lock);
if (numNodes == 0) {
    head = newNode;
    tail = newNode;
    tail->next = NULL;
    numNodes++;
}
else {
    tail->next = newNode;
    tail = newNode;
    tail->next = NULL;
    numNodes++;
}
pthread_mutex_unlock(&list_lock);

或遍历列表时:

//check if peer is in the file list
pthread_mutex_lock(&list_lock);
ptr = head;
while(ptr != NULL) {
    // if peer is found
    if((strcmp(ptr->ip, temp_ip) == 0) && ptr->port == temp_port){
        cmd = htonl(200); //set cmd to 1
        break;
    }
    ptr = ptr->next;
}
pthread_mutex_unlock(&list_lock);

由于您的列表访问相对较少,因此应该没问题。如果遇到由此锁争用引起的可伸缩性问题,您可以转向更复杂的锁定方案。