C 套接字编程中使用链表的线程安全
Thread safety with a linked list in C Socket Programming
我正在使用线程和 TCP 在 C 中实现 P2P 文件传输。我有一个跟踪器,它跟踪所有连接到它的对等点。当对等方首次连接时,它将其文件从其当前目录发送到跟踪器,跟踪器将所有对等方的文件放入共享链表中。每个对等点在跟踪器中都有自己的线程,当对等点退出时,跟踪器关闭该对等点的连接并从链表中删除该对等点的所有文件。我关心的是在添加和删除节点时锁定链表。我希望能够安全地从各种线程更改链表。我已经从我的链表中将我的代码写到 add/delete 并且还搜索(不确定我在搜索时是否需要使用锁定) 我如何修改我的代码以确保线程之间的链接安全?
最简单的解决方案是在对列表进行任何访问之前锁定一个 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);
由于您的列表很少被访问,因此应该没问题。如果您 运行 遇到由争用此锁引起的可伸缩性问题,您可以转向更复杂的锁定方案。
请考虑使用互斥来保护数据或其他资源免受并发访问。
在 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
指针)并使用(reading/writing)存储在节点中的数据时;
- 写操作:改变节点:
next
指针和存储在节点中的数据。
此外,相同的互斥锁可用于保护对 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);
}
值得注意的是,减少锁争用 — 并发应用程序性能调优的重要组成部分。
Socket相关问题
send()
和 recv()
函数不保证 所有指定的字节数 将是 sent/received 一次函数调用。
应使用适当的循环来发送或接收所需(预期)的字节数。更多详情请参考文章:TCP/IP client-server application: exchange with string messages.
此外,请注意此类结构:
n = recv(newsockfd, buffer, sizeof(buffer), 0);
// ...
buffer[n] = '[=11=]';
要接收的预期字节数(或在本例中为字符数)应为 sizeof(buffer) - 1
。否则,数据丢失:最后一个字符被终止空字符覆盖。
我正在使用线程和 TCP 在 C 中实现 P2P 文件传输。我有一个跟踪器,它跟踪所有连接到它的对等点。当对等方首次连接时,它将其文件从其当前目录发送到跟踪器,跟踪器将所有对等方的文件放入共享链表中。每个对等点在跟踪器中都有自己的线程,当对等点退出时,跟踪器关闭该对等点的连接并从链表中删除该对等点的所有文件。我关心的是在添加和删除节点时锁定链表。我希望能够安全地从各种线程更改链表。我已经从我的链表中将我的代码写到 add/delete 并且还搜索(不确定我在搜索时是否需要使用锁定) 我如何修改我的代码以确保线程之间的链接安全?
最简单的解决方案是在对列表进行任何访问之前锁定一个 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);
由于您的列表很少被访问,因此应该没问题。如果您 运行 遇到由争用此锁引起的可伸缩性问题,您可以转向更复杂的锁定方案。
请考虑使用互斥来保护数据或其他资源免受并发访问。
在 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
指针)并使用(reading/writing)存储在节点中的数据时; - 写操作:改变节点:
next
指针和存储在节点中的数据。
此外,相同的互斥锁可用于保护对 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);
}
值得注意的是,减少锁争用 — 并发应用程序性能调优的重要组成部分。
Socket相关问题
send()
和 recv()
函数不保证 所有指定的字节数 将是 sent/received 一次函数调用。
应使用适当的循环来发送或接收所需(预期)的字节数。更多详情请参考文章:TCP/IP client-server application: exchange with string messages.
此外,请注意此类结构:
n = recv(newsockfd, buffer, sizeof(buffer), 0);
// ...
buffer[n] = '[=11=]';
要接收的预期字节数(或在本例中为字符数)应为 sizeof(buffer) - 1
。否则,数据丢失:最后一个字符被终止空字符覆盖。