在多线程或多进程编程中,线程与子进程之间的通信是确保程序高效运行的关键。无论是为了提高并发性能,还是为了实现复杂的任务分解,掌握线程与子进程通信的技巧至关重要。本文将详细介绍线程与子进程通信的方法,并通过实例代码帮助读者更好地理解这些技巧。
线程间通信
线程间通信(Inter-Thread Communication,ITC)通常用于在同一个进程中的多个线程之间交换数据或同步操作。以下是一些常见的线程间通信方法:
1. 共享内存
共享内存是一种最直接有效的线程间通信方式。它允许多个线程访问同一块内存区域,从而实现数据的共享。
#include <pthread.h>
#include <stdio.h>
int shared_data = 0;
void *thread_function(void *arg) {
// 线程1
pthread_mutex_lock(&mutex);
shared_data = 1;
printf("Thread 1: Shared data set to 1\n");
pthread_mutex_unlock(&mutex);
// 线程2
pthread_mutex_lock(&mutex);
shared_data = 2;
printf("Thread 2: Shared data set to 2\n");
pthread_mutex_unlock(&mutex);
return NULL;
}
int main() {
pthread_t thread1, thread2;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_create(&thread1, NULL, thread_function, NULL);
pthread_create(&thread2, NULL, thread_function, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("Main: Shared data is %d\n", shared_data);
pthread_mutex_destroy(&mutex);
return 0;
}
2. 条件变量
条件变量用于线程间的同步,它允许一个或多个线程等待某个条件成立,直到其他线程改变该条件。
#include <pthread.h>
#include <stdio.h>
int condition = 0;
void *thread_function(void *arg) {
if (arg == (void *)1) {
// 线程1
pthread_mutex_lock(&mutex);
while (condition != 1) {
pthread_cond_wait(&cond, &mutex);
}
printf("Thread 1: Condition met\n");
pthread_mutex_unlock(&mutex);
} else {
// 线程2
pthread_mutex_lock(&mutex);
condition = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t thread1, thread2;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_create(&thread1, NULL, thread_function, (void *)1);
pthread_create(&thread2, NULL, thread_function, (void *)2);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
子进程间通信
子进程间通信(Inter-Process Communication,IPC)通常用于在父进程与子进程之间,或者多个子进程之间交换数据。
1. 管道(Pipes)
管道是一种简单的IPC机制,允许进程间通过一个管道进行数据的单向传输。
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
int pipe_fd[2];
pid_t cpid;
if (pipe(pipe_fd) == -1) {
perror("pipe");
return 1;
}
cpid = fork();
if (cpid == -1) {
perror("fork");
return 1;
}
if (cpid == 0) {
// 子进程
close(pipe_fd[0]); // 关闭读端
write(pipe_fd[1], "Hello, parent!\n", 16);
close(pipe_fd[1]); // 关闭写端
} else {
// 父进程
close(pipe_fd[1]); // 关闭写端
char buffer[1024];
read(pipe_fd[0], buffer, 1024);
printf("Parent: %s", buffer);
close(pipe_fd[0]); // 关闭读端
}
wait(NULL); // 等待子进程结束
return 0;
}
2. 命名管道(Named Pipes)
命名管道是一种特殊的文件,允许在任意两个进程之间进行通信。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
int main() {
int pipe_fd;
pid_t cpid;
// 创建命名管道
if (mkfifo("myfifo", 0666) == -1) {
perror("mkfifo");
return 1;
}
cpid = fork();
if (cpid == -1) {
perror("fork");
return 1;
}
if (cpid == 0) {
// 子进程
close(STDOUT_FILENO); // 关闭标准输出
dup(pipe_fd); // 将管道文件描述符复制到标准输出
close(pipe_fd); // 关闭管道文件描述符
write(STDOUT_FILENO, "Hello, parent!\n", 16);
} else {
// 父进程
close(pipe_fd); // 关闭管道文件描述符
pipe_fd = open("myfifo", O_RDONLY);
char buffer[1024];
read(pipe_fd, buffer, 1024);
printf("Parent: %s", buffer);
close(pipe_fd); // 关闭管道文件描述符
}
// 删除命名管道
unlink("myfifo");
wait(NULL); // 等待子进程结束
return 0;
}
3. 消息队列
消息队列允许进程间通过消息传递数据。每个消息都有一个类型和一个数据部分。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MSGKEY 1234
#define MSGSIZE 128
struct message {
long msg_type;
char msg_text[MSGSIZE];
};
int main() {
int msgid;
struct message msg;
// 创建消息队列
msgid = msgget(MSGKEY, 0666 | IPC_CREAT);
if (msgid == -1) {
perror("msgget");
exit(1);
}
// 发送消息
msg.msg_type = 1;
snprintf(msg.msg_text, MSGSIZE, "Hello, parent!");
msgsnd(msgid, &msg, MSGSIZE, 0);
printf("Sent message: %s\n", msg.msg_text);
// 接收消息
msgrcv(msgid, &msg, MSGSIZE, 1, 0);
printf("Received message: %s\n", msg.msg_text);
// 删除消息队列
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
4. 信号量
信号量是一种用于进程间同步的机制,它可以实现进程间的互斥和同步。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define SEMKEY 1234
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
int main() {
int semid;
union semun arg;
struct sembuf sop;
// 创建信号量集
semid = semget(SEMKEY, 1, 0666 | IPC_CREAT);
if (semid == -1) {
perror("semget");
exit(1);
}
// 初始化信号量
arg.val = 1;
if (semctl(semid, 0, SETVAL, arg) == -1) {
perror("semctl");
exit(1);
}
// 等待信号量
sop.sem_num = 0;
sop.sem_op = -1; // P操作
sop.sem_flg = 0;
if (semop(semid, &sop, 1) == -1) {
perror("semop");
exit(1);
}
printf("Semaphore acquired\n");
// 释放信号量
sop.sem_op = 1; // V操作
if (semop(semid, &sop, 1) == -1) {
perror("semop");
exit(1);
}
// 删除信号量集
if (semctl(semid, 0, IPC_RMID, arg) == -1) {
perror("semctl");
exit(1);
}
return 0;
}
通过以上介绍,相信读者已经对线程与子进程通信有了更深入的了解。在实际编程中,选择合适的通信方式对于提高程序性能和可靠性至关重要。希望本文能帮助读者在多线程和多进程编程中更好地实现高效数据交互。
