Linux应用层下实现休眠与唤醒

在学习之前首先我们要先理解什么是休眠与唤醒。

一、简介

休眠,在应用层中意味着一个线程或者进程主动让出CPU,不继续执行,直到某个条件被满足才继续执行。这就是休眠休眠的目的就是等待资源或时间。唤醒,指的是休眠中的线程被重新激活,继续执行后续的任务。

二、作用

在了解休眠与唤醒以后,那么休眠与唤醒的作用是什么?我们在什么情况下要用到休眠与唤醒。首先休眠与唤醒的主要作用是在任务或者线程之间协调资源的使用,避免不必要的CPU占用,提高程序的执行效率。

这边简单的举几个休眠与唤醒的场景

  • 等待文件或网络 I/O 完成
  • 线程间通过条件变量进行同步和通讯
  • 等待用户输入事件
  • 定时任务(通过休眠线程实现)

三、具体场景和实现步骤

这里举一个实际开发中的场景:HTTP 服务器的请求处理。,在一个简单的HTTP服务器中,当没有请求是,服务器线程会进入休眠状态以节省CPU资源,而当有请求到达时,服务器会唤醒处理该请求。

下面是以恶搞用C语言编写的简单的HTTP服务器示例,使用POSIX线程和条件变量实现请求的休眠与唤醒机制

首先是头文件,请求队列,宏定义的定义

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

// 请求队列
char request_queue[BUFFER_SIZE][BUFFER_SIZE];
int request_count = 0;
pthread_mutex_t mutex;
pthread_cond_t request_available;

处理请求的线程函数

// 处理请求的线程函数
void* request_handler(void* arg) {
    while (1) {
        pthread_mutex_lock(&mutex); // 上锁
        // 如果没有请求,休眠
        while (request_count == 0) {
            printf("没有请求,处理线程休眠...\n");
            pthread_cond_wait(&request_available, &mutex);
        }

        // 处理请求
        char request[BUFFER_SIZE];
        strcpy(request, request_queue[--request_count]); // 取出请求
        printf("处理请求:%s\n", request);
        pthread_mutex_unlock(&mutex); // 解锁

        // 模拟请求处理延迟
        sleep(2);
    }
}

首先当队列中没有请求时,会使用pthread_cond_wait() 进入休眠,当有新的请求时另一个线程通过pthread_cond_signal() 发出信号,唤醒休眠处理的线程

监听连接的线程函数

// 监听连接的线程函数
void* connection_listener(void* arg) {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    
    // 创建套接字
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("Socket failed");
        exit(EXIT_FAILURE);
    }

    // 限制端口复用
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 绑定套接字
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("Bind failed");
        exit(EXIT_FAILURE);
    }

    // 开始监听
    if (listen(server_fd, 3) < 0) {
        perror("Listen failed");
        exit(EXIT_FAILURE);
    }

    printf("服务器启动,等待连接...\n");
    while (1) {
        // 等待连接
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            perror("Accept failed");
            continue;
        }

        // 读取请求
        char buffer[BUFFER_SIZE] = {0};
        read(new_socket, buffer, BUFFER_SIZE);
        printf("接收到请求:%s\n", buffer);

        pthread_mutex_lock(&mutex); // 上锁
        // 将请求放入队列
        strcpy(request_queue[request_count++], buffer);
        // 唤醒处理线程
        pthread_cond_signal(&request_available);
        pthread_mutex_unlock(&mutex); // 解锁

        close(new_socket); // 关闭套接字
    }
}

首先使用IPv4和TCP协议创建一个套接字,然后定义一个定义一个 sockaddr_in 结构体,表示服务器的地址信息。使用bind绑定服务器的套接字文件描述符server_fdaddress,将服务器绑定到指定的IP地址和端口上。使用listen函数使服务器的套接字进入监听专题,准备接收客户端的连接请求。while循环,服务器会无限循环,持续监听客户端连接,不断处理客户端的请求。accept():服务器阻塞等待客户端的连接。当有客户端发起连接时,accept() 返回一个新的套接字 new_socket,用于与客户端通信。然后使用read函数读取客户端发送的数据到buffer中。最后为了确保线程安全,在访问共享资源请求队列和请求技术时要加锁。

主函数

int main() {
    pthread_t handler_thread, listener_thread;

    // 初始化互斥锁和条件变量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&request_available, NULL);

    // 创建处理请求的线程和监听连接的线程
    pthread_create(&handler_thread, NULL, request_handler, NULL);
    pthread_create(&listener_thread, NULL, connection_listener, NULL);

    // 等待线程结束
    pthread_join(handler_thread, NULL);
    pthread_join(listener_thread, NULL);

    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&request_available);

    return 0;
}

在主函数中主要是初始化互斥锁和条件变量,创建线程,调用pthread_join() 等待两个线程执行完。

四、整体代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

// 请求队列
char request_queue[BUFFER_SIZE][BUFFER_SIZE];
int request_count = 0;
pthread_mutex_t mutex;
pthread_cond_t request_available;

// 处理请求的线程函数
void* request_handler(void* arg) {
    while (1) {
        pthread_mutex_lock(&mutex); // 上锁
        // 如果没有请求,休眠
        while (request_count == 0) {
            printf("没有请求,处理线程休眠...\n");
            pthread_cond_wait(&request_available, &mutex);
        }

        // 处理请求
        char request[BUFFER_SIZE];
        strcpy(request, request_queue[--request_count]); // 取出请求
        printf("处理请求:%s\n", request);
        pthread_mutex_unlock(&mutex); // 解锁

        // 模拟请求处理延迟
        sleep(2);
    }
}

// 监听连接的线程函数
void* connection_listener(void* arg) {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    
    // 创建套接字
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("Socket failed");
        exit(EXIT_FAILURE);
    }

    // 限制端口复用
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 绑定套接字
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("Bind failed");
        exit(EXIT_FAILURE);
    }

    // 开始监听
    if (listen(server_fd, 3) < 0) {
        perror("Listen failed");
        exit(EXIT_FAILURE);
    }

    printf("服务器启动,等待连接...\n");
    while (1) {
        // 等待连接
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            perror("Accept failed");
            continue;
        }

        // 读取请求
        char buffer[BUFFER_SIZE] = {0};
        read(new_socket, buffer, BUFFER_SIZE);
        printf("接收到请求:%s\n", buffer);

        pthread_mutex_lock(&mutex); // 上锁
        // 将请求放入队列
        strcpy(request_queue[request_count++], buffer);
        // 唤醒处理线程
        pthread_cond_signal(&request_available);
        pthread_mutex_unlock(&mutex); // 解锁

        close(new_socket); // 关闭套接字
    }
}

int main() {
    pthread_t handler_thread, listener_thread;

    // 初始化互斥锁和条件变量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&request_available, NULL);

    // 创建处理请求的线程和监听连接的线程
    pthread_create(&handler_thread, NULL, request_handler, NULL);
    pthread_create(&listener_thread, NULL, connection_listener, NULL);

    // 等待线程结束
    pthread_join(handler_thread, NULL);
    pthread_join(listener_thread, NULL);

    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&request_available);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值