
在C语言中实现阻塞有多种方法,包括使用系统调用、条件变量、信号量等。最常见的方式是通过系统调用如 sleep、select、poll 等来实现阻塞。 其中,sleep 是最简单的方法,用于让程序暂停执行一段时间,而 select 和 poll 则用于监控多个文件描述符,等待它们变为可读、可写或有错误发生。下面将详细介绍如何使用 select 实现阻塞。
一、使用 sleep 函数实现阻塞
sleep 函数是一个简单的系统调用,用于让当前线程暂停执行指定的秒数。它非常适合用于需要简单延迟的场景。
示例代码:
#include <stdio.h>
#include <unistd.h>
int main() {
printf("Startn");
sleep(5); // 阻塞5秒
printf("Endn");
return 0;
}
在这个例子中,程序会在输出 "Start" 后暂停5秒钟,然后输出 "End"。
二、使用 select 函数实现阻塞
select 函数提供了更为灵活的阻塞机制,适用于监控多个文件描述符的场景。
示例代码:
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>
int main() {
fd_set read_fds;
struct timeval timeout;
// 清空文件描述符集合
FD_ZERO(&read_fds);
// 添加标准输入(文件描述符 0)到集合
FD_SET(STDIN_FILENO, &read_fds);
// 设置超时时间
timeout.tv_sec = 10;
timeout.tv_usec = 0;
// 阻塞直到标准输入有数据或超时
int result = select(STDIN_FILENO + 1, &read_fds, NULL, NULL, &timeout);
if (result == -1) {
perror("select");
return 1;
} else if (result == 0) {
printf("Timeout occurred, no data inputn");
} else {
printf("Data is available to readn");
}
return 0;
}
在这个例子中,程序会阻塞最多10秒,等待标准输入有数据可读。如果在10秒内没有数据可读,则程序输出 "Timeout occurred, no data input"。
三、使用 poll 函数实现阻塞
poll 函数是 select 的增强版,提供了类似的功能但更为高效。它适用于监控大量文件描述符的场景。
示例代码:
#include <stdio.h>
#include <poll.h>
#include <unistd.h>
int main() {
struct pollfd fds[1];
// 设置标准输入为待监控的文件描述符
fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN;
// 阻塞等待标准输入有数据或超时(10秒)
int result = poll(fds, 1, 10000);
if (result == -1) {
perror("poll");
return 1;
} else if (result == 0) {
printf("Timeout occurred, no data inputn");
} else {
if (fds[0].revents & POLLIN) {
printf("Data is available to readn");
}
}
return 0;
}
在这个例子中,程序会阻塞最多10秒,等待标准输入有数据可读。如果在10秒内没有数据可读,则程序输出 "Timeout occurred, no data input"。
四、使用条件变量实现阻塞
条件变量是一种同步机制,适用于需要线程间通信的场景。它通常与互斥锁一起使用。
示例代码:
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void* thread_func(void* arg) {
printf("Thread waiting for signal...n");
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
printf("Thread received signal!n");
return NULL;
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, thread_func, NULL);
// 主线程睡眠2秒
sleep(2);
// 发出条件变量信号
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
// 等待子线程结束
pthread_join(thread, NULL);
return 0;
}
在这个例子中,子线程会阻塞等待条件变量信号,主线程在2秒后发送信号,子线程接收到信号后继续执行。
五、使用信号量实现阻塞
信号量是一种同步机制,适用于需要控制资源访问的场景。
示例代码:
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
sem_t sem;
void* thread_func(void* arg) {
printf("Thread waiting for semaphore...n");
sem_wait(&sem);
printf("Thread acquired semaphore!n");
return NULL;
}
int main() {
pthread_t thread;
sem_init(&sem, 0, 0);
pthread_create(&thread, NULL, thread_func, NULL);
// 主线程睡眠2秒
sleep(2);
// 释放信号量
sem_post(&sem);
// 等待子线程结束
pthread_join(thread, NULL);
sem_destroy(&sem);
return 0;
}
在这个例子中,子线程会阻塞等待信号量,主线程在2秒后释放信号量,子线程接收到信号量后继续执行。
六、阻塞与非阻塞的对比
阻塞操作通常用于等待某种条件的满足,例如数据的到达或资源的可用。非阻塞操作则会立即返回,无论条件是否满足。选择使用阻塞还是非阻塞操作取决于具体的应用场景和性能要求。
示例代码(非阻塞):
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
// 设置标准输入为非阻塞模式
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
char buffer[128];
int bytes_read;
while (1) {
bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
if (bytes_read == -1) {
perror("read");
sleep(1); // 暂停1秒后重试
} else if (bytes_read == 0) {
printf("End of inputn");
break;
} else {
buffer[bytes_read] = '