在多线程或多进程的应用程序中,线程与进程之间的通信是确保任务协同和资源共享的关键。本文将深入探讨线程与进程通信的技巧,并通过实际应用案例展示其高效性。
1. 线程间通信
线程间通信(Inter-thread Communication)通常发生在同一进程中的多个线程之间。以下是几种常见的线程间通信机制:
1.1 共享内存
共享内存是一种高效的线程间通信方式,允许线程访问同一块内存区域。
代码示例:
#include <pthread.h>
#include <stdio.h>
int counter = 0;
void* increment_counter(void* arg) {
for (int i = 0; i < 100000; i++) {
__atomic_add_fetch(&counter, 1, __ATOMIC_SEQ_CST);
}
return NULL;
}
int main() {
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, increment_counter, NULL);
pthread_create(&thread2, NULL, increment_counter, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("Counter: %d\n", counter);
return 0;
}
1.2 互斥锁(Mutex)
互斥锁用于保护共享资源,确保同一时间只有一个线程可以访问该资源。
代码示例:
#include <pthread.h>
#include <stdio.h>
int counter = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* increment_counter(void* arg) {
for (int i = 0; i < 100000; i++) {
pthread_mutex_lock(&mutex);
counter++;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
// ... (same as above)
}
2. 进程间通信
进程间通信(Inter-process Communication,IPC)允许不同进程之间交换数据。
2.1 命名管道(Named Pipes)
命名管道是一种高效的进程间通信方式,允许两个或多个进程之间进行全双工数据交换。
代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main() {
int fd = mkfifo("temp_fifo", 0666);
if (fd == -1) {
perror("mkfifo");
exit(EXIT_FAILURE);
}
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid_t cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { // Child process
close(pipefd[1]); // Close unused write end
dup2(pipefd[0], STDIN_FILENO); // Redirect stdin to pipe
execlp("grep", "grep", "grep", (char*)NULL);
} else {
close(pipefd[0]); // Close unused read end
write(pipefd[1], "Hello, world!\n", 14);
close(pipefd[1]);
}
return 0;
}
2.2 信号量(Semaphores)
信号量是一种用于进程间同步的机制,可以控制对共享资源的访问。
代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
void init_sem(sem_t *sem_id) {
union semun arg;
arg.val = 1;
semctl(sem_id, 0, SETVAL, arg);
}
int main() {
key_t key = ftok("semfile", 65);
int sem_id = semget(key, 1, 0666 | IPC_CREAT);
init_sem((sem_t*)sem_id);
// ... (rest of the code)
return 0;
}
3. 应用案例
以下是一个简单的应用案例,展示如何使用共享内存和互斥锁实现线程安全的计数器:
应用场景: 在一个并发应用程序中,多个线程需要更新一个全局计数器。
代码示例:
#include <pthread.h>
#include <stdio.h>
int counter = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* increment_counter(void* arg) {
for (int i = 0; i < 100000; i++) {
pthread_mutex_lock(&mutex);
counter++;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, increment_counter, NULL);
pthread_create(&thread2, NULL, increment_counter, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("Counter: %d\n", counter);
return 0;
}
在这个案例中,我们使用了互斥锁来保护计数器,确保在多线程环境中,计数器的更新是线程安全的。
通过上述讨论,我们可以看到,线程与进程间的通信对于构建高效、可靠的应用程序至关重要。掌握这些技巧,可以帮助开发者更好地利用多线程和多进程的优势,实现复杂的系统设计。
