在现代计算机编程中,进程和线程是执行程序的基本单位。它们之间的通信对于程序的正确运行和效率至关重要。本文将探讨进程与线程间通信的实用技巧,帮助开发者更好地理解和实现高效通信。
1. 管道(Pipes)
管道是进程间通信(IPC)的一种简单而有效的方式。它允许一个进程将数据发送到另一个进程,后者可以从管道中读取这些数据。管道可以是无名管道或命名管道。
1.1 无名管道
无名管道是用于具有亲缘关系的进程间通信的,如父子进程。以下是使用无名管道进行通信的示例代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
int pipefd[2];
pid_t cpid;
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { // 子进程
close(pipefd[1]); // 关闭写端
read(pipefd[0], &cpid, sizeof(cpid)); // 读取数据
printf("Received %d\n", cpid);
close(pipefd[0]); // 关闭读端
exit(EXIT_SUCCESS);
} else { // 父进程
close(pipefd[0]); // 关闭读端
write(pipefd[1], &cpid, sizeof(cpid)); // 写入数据
close(pipefd[1]); // 关闭写端
wait(NULL); // 等待子进程结束
}
return 0;
}
1.2 命名管道(FIFO)
命名管道是一种持久的IPC机制,可以在不相关的进程之间进行通信。以下是使用命名管道进行通信的示例代码:
import os
import msvcrt
import time
pipe_path = 'temp_pipe'
# 创建命名管道
if not os.path.exists(pipe_path):
os.mkfifo(pipe_path)
# 父进程
with open(pipe_path, 'w') as pipe:
for i in range(5):
pipe.write(f"Hello from parent {i}\n")
time.sleep(1)
# 子进程
with open(pipe_path, 'r') as pipe:
while True:
data = pipe.readline()
if not data:
break
print(f"Received from parent: {data.strip()}")
2. 套接字(Sockets)
套接字是进程间通信的另一种重要方式,它允许不同主机上的进程进行通信。以下是使用套接字进行通信的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
int sock;
struct sockaddr_in servaddr, cliaddr;
socklen_t len;
char buf[1024];
// 创建套接字
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
// 设置服务器地址结构
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8080);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
// 绑定套接字
if (bind(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
// 监听连接
if (listen(sock, 5) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
// 接受连接
len = sizeof(cliaddr);
int connfd = accept(sock, (struct sockaddr *)&cliaddr, &len);
if (connfd < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
// 读取客户端数据
read(connfd, buf, sizeof(buf));
printf("Received: %s\n", buf);
// 关闭套接字
close(connfd);
close(sock);
return 0;
}
3. 共享内存(Shared Memory)
共享内存是进程间通信的另一种高效方式,它允许多个进程访问同一块内存区域。以下是使用共享内存进行通信的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
const char *name = "/my_shared_memory";
const char *message = "Hello from shared memory!";
// 打开共享内存对象
int fd = open(name, O_RDWR | O_CREAT, 0666);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
// 设置共享内存大小
if (ftruncate(fd, sizeof(message)) == -1) {
perror("ftruncate");
exit(EXIT_FAILURE);
}
// 映射共享内存
char *shared_memory = mmap(NULL, sizeof(message), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shared_memory == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
// 写入共享内存
strcpy(shared_memory, message);
// 关闭文件描述符
close(fd);
// 读取共享内存
printf("Shared memory content: %s\n", shared_memory);
// 解除映射
munmap(shared_memory, sizeof(message));
return 0;
}
4. 消息队列(Message Queues)
消息队列是进程间通信的另一种方式,它允许进程将消息发送到队列中,其他进程可以从中读取消息。以下是使用消息队列进行通信的示例代码:
import os
import time
import queue
import threading
# 创建消息队列
msg_queue = queue.Queue()
def producer():
for i in range(5):
msg_queue.put(f"Message {i}")
print(f"Produced: {msg_queue.qsize()}")
def consumer():
while True:
msg = msg_queue.get()
print(f"Consumed: {msg}")
time.sleep(1)
# 创建并启动线程
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)
producer_thread.start()
consumer_thread.start()
producer_thread.join()
consumer_thread.join()
5. 信号量(Semaphores)
信号量是一种用于进程间同步的机制,它允许进程在访问共享资源时进行协调。以下是使用信号量进行通信的示例代码:
import threading
import time
# 创建信号量
semaphore = threading.Semaphore(1)
def producer():
for i in range(5):
with semaphore:
print(f"Produced: {i}")
time.sleep(1)
def consumer():
for i in range(5):
with semaphore:
print(f"Consumed: {i}")
time.sleep(1)
# 创建并启动线程
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)
producer_thread.start()
consumer_thread.start()
producer_thread.join()
consumer_thread.join()
总结
进程与线程间通信是现代计算机编程中不可或缺的一部分。本文介绍了管道、套接字、共享内存、消息队列和信号量等实用技巧,希望对您的编程实践有所帮助。在实际应用中,选择合适的通信机制取决于具体场景和需求。
