在多线程或多进程的程序设计中,线程与进程间的通信是确保程序协调一致、高效运行的关键。下面,我将揭秘五大实用通信技巧,帮助你更好地理解和实现线程与进程间的通信。
1. 使用管道(Pipe)
管道是线程或进程间进行通信的一种简单有效的方式。在Unix系统中,管道通常通过pipe()函数创建,允许数据从父进程流向子进程。
代码示例:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
pid_t cpid = fork();
if (cpid == -1) {
perror("fork");
return 1;
}
if (cpid == 0) { // 子进程
close(pipefd[0]); // 关闭读端
write(pipefd[1], "Hello, parent!\n", 16);
close(pipefd[1]);
} else { // 父进程
close(pipefd[1]); // 关闭写端
char buf[1024];
read(pipefd[0], buf, sizeof(buf) - 1);
printf("Received: %s\n", buf);
close(pipefd[0]);
wait(NULL); // 等待子进程结束
}
return 0;
}
2. 使用共享内存(Shared Memory)
共享内存允许多个进程或线程共享同一块内存空间,从而实现高效的数据交换。
代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define SHM_SIZE 1024
int main() {
int shm_fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0666);
ftruncate(shm_fd, SHM_SIZE);
void *addr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (addr == MAP_FAILED) {
perror("mmap");
exit(1);
}
// 读写共享内存
printf("Shared memory content: %s\n", (char *)addr);
// 关闭共享内存
munmap(addr, SHM_SIZE);
close(shm_fd);
return 0;
}
3. 使用消息队列(Message Queue)
消息队列是一种先进先出的数据结构,允许线程或进程将消息发送到队列中,其他线程或进程可以从队列中读取消息。
代码示例:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct msgbuf {
long msgtype;
char msgtext[100];
};
int main() {
key_t key = ftok("msgqueuefile", 65);
int msgid = msgget(key, 0666 | IPC_CREAT);
struct msgbuf msg;
msg.msgtype = 1;
snprintf(msg.msgtext, sizeof(msg.msgtext), "Hello, message queue!");
msgsnd(msgid, &msg, sizeof(msg.msgtext), 0);
// 接收消息
msgrcv(msgid, &msg, sizeof(msg.msgtext), 1, 0);
printf("Received message: %s\n", msg.msgtext);
return 0;
}
4. 使用信号量(Semaphore)
信号量是一种同步机制,用于保护共享资源,确保一次只有一个线程或进程可以访问该资源。
代码示例:
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void *thread_func(void *arg) {
pthread_mutex_lock(&lock);
printf("Thread %ld is running.\n", (long)arg);
pthread_mutex_unlock(&lock);
return NULL;
}
int main() {
pthread_t threads[5];
for (long i = 0; i < 5; i++) {
if (pthread_create(&threads[i], NULL, thread_func, (void *)i) != 0) {
perror("pthread_create");
return 1;
}
}
for (int i = 0; i < 5; i++) {
pthread_join(threads[i], NULL);
}
pthread_mutex_destroy(&lock);
return 0;
}
5. 使用套接字(Socket)
套接字是一种用于进程间通信的机制,可以用于不同主机上的进程之间进行通信。
代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
void *handle_client(void *socket_desc) {
int sock = *(int*)socket_desc;
char buffer[1024];
int read_size;
while ((read_size = recv(sock, buffer, 1024, 0)) > 0) {
printf("Client: %s\n", buffer);
send(sock, "Hello from server", 26, 0);
}
if (read_size == 0) {
puts("Client disconnected");
} else if (read_size == -1) {
perror("recv failed");
}
close(sock);
free(socket_desc);
return 0;
}
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
// 创建socket文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 强制绑定到端口8080
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// 绑定socket到端口
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听socket
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
// 接受并处理客户端连接
while ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))) {
puts("Connection accepted");
pthread_t thread_id;
if (pthread_create(&thread_id, NULL, handle_client, (void*)&new_socket) != 0) {
perror("could not create thread");
}
}
if (new_socket < 0) {
perror("accept failed");
exit(EXIT_FAILURE);
}
return 0;
}
通过以上五种实用通信技巧,你可以有效地在多线程或多进程的程序中实现高效的数据交换和同步。希望这些技巧能帮助你解决实际问题,让你的程序更加健壮和高效。
