IO进程学习——6

一、线程(补)

1.pthread_join 函数

函数功能:

​ 等待线程结束并回收线程产生的资源

函数头文件:

​ #include <pthread.h>

函数原型:

int pthread_join(pthread_t thread, void **retval);

函数参数说明:

  1. thread: 需要处理的线程的线程 id
  2. retval: 获取线程退出的状态信息

函数返回值说明:

​ 成功:返回 0

​ 失败:返回错误码

用法示例:

/*===============================================
*   文件名称:pthread_join.c
*   创 建 者:青木莲华
*   创建日期:2025年08月11日
*   描    述:
================================================*/
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void *func(void *arg)
{
    int *p = (int *)arg;
    while(1)
    {
        sleep(1); 
        printf("------%d------\n",*p);
        if(*p == 3)
        {
            char *retval = "The thread is exit!";
            printf("%p\n",retval);
            pthread_exit((void *)retval);
        }
        (*p)++;
    }
}


int main(int argc, char *argv[])
{ 
    int a = 0;
    pthread_t tid;
    void *retval = NULL;
    pthread_create(&tid,NULL,func,(void *)&a);
    pthread_join(tid,&retval);
    printf("%p\n",retval);
    printf("%s\n",(char *)retval);
    while(1);
    return 0;
} 

运行截图

alt

2.pthread_cancel 函数

函数功能:

​ 退出函数函数

函数头文件:

​ #include <pthread.h>

函数原型:

​ int pthread_cancel(pthread_t thread);

函数参数说明:

​ 1.thread:需要取消的线程的线程ID

函数返回值说明:

​ 成功:返回0

​ 失败:返回非零的错误码

3.pthread_detach 函数

函数的功能:

​ 线程分离函数,线程结束后资源将自动回收 函数使用需要的头文件: ​ #include <pthread.h> 函数原型: ​ int pthread_detach (pthread_t thread); 函数参数说明: ​ 1.thread: 需要分离的线程的线程 ID 函数返回值说明: ​ 成功:返回 0 ​ 失败:返回非零的错误码

二、线程的通信

线程间的通信非常简单,原因是多个线程共用同一片进程空间,所以数据的

交互简单,但是数据交互时需要避免资源竞争问题

如何解决资源竞争问题?

同步互斥机制

同步互斥

1.同步

(1)线程间的同步 PV操作

信号量

​ 代表某一类资源,其值表示系统中该资源的数量

​ 是一个受保护的变量,只能通过三种操作来访问 初始化

​ P操作(申请资源) ​ V操作(释放资源)

接口函数

sem_init

函数的功能:

​ 生产资源,V操作,生产者,信号量的资源值+1

函数使用需要的头文件:

​ #include <semaphore.h> 函数原型:

int sem_init(sem_t *sem, int pshared, unsigned int value);

函数参数说明:

​ 1.sem:信号量变量指针

​ 2.pshared:设置是进程间同步还是线程间同步,0为线程间同步

​ 3.value:设置初始可以使用资源的值

函数返回值说明:

​ 成功:返回0

​ 失败:返回-1,并且设置全局错误号

sem_post

函数功能:

​ 生产资源,V操作,生产者,信号量的资源值+1函数

函数头文件:

​ #include <semaphore.h>

函数原型:

int sem_post(sem_t *sem);

函数参数说明:

​ 1.sem:信号量变量指针

函数返回值说明:

​ 成功:返回0

​ 失败:返回-1,并且设置全局错误号

sem_wait

函数功能:

​ 同步机制中的消费者功能,为信号量-1(P操作)

函数头文件:

​ #include <semaphore.h>

函数原型:

int sem_wait(sem_t *sem);

函数参数说明:

​ 1.sem:信号量变量指针

函数返回值说明:

​ 成功:返回0

​ 失败:返回-1,并且设置全局错误号

(2)练习 多任务需要按照指定顺序运行

/*===============================================
*   文件名称:sem_n.c
*   创 建 者:青木莲华
*   创建日期:2025年08月11日
*   描    述:资源同步 sem_t 多信号量
================================================*/
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>



//同步信号量1  同步线程1(V)和线程2(P)
sem_t s1;
//同步信号量2  同步线程2(V)和线程3(P)
sem_t s2;
//信号量1 生产变量
int flag = 0;
//线程1
void *func1(void *arg)
{
    while(1)
    {
        scanf("%d",&flag);
        if(flag == 1)
        {
            flag = 0;
            printf("线程1:sem1---V操作\n");
            sem_post(&s1);   
        }
        else if(flag == 2)
        {
            flag = 0;
            printf("线程1:sem2---V操作\n");
            sem_post(&s2);
        }
    }
}
//线程2
void *func2(void *arg)
{
    while(1)
    {
        sem_wait(&s1);
        printf("线程2:sem1---P操作\n");
        printf("线程2:sem2---V操作\n");
        sem_post(&s2);
    }
}
//线程3
void *func3(void *arg)
{
    while(1)
    {
        sem_wait(&s2);
        printf("线程3:sem2---P操作\n");
    }
}

int main(int argc, char *argv[])
{ 
    //初始化信号量
    //第二个参数设置为0表示线程间同步
    sem_init(&s1,0,0);
    sem_init(&s2,0,0);
    //线程1
    pthread_t tid1;
    pthread_create(&tid1,NULL,func1,NULL);
    //线程2
    pthread_t tid2;
    pthread_create(&tid2,NULL,func2,NULL);
    //线程3
    pthread_t tid3;
    pthread_create(&tid3,NULL,func3,NULL);

    while(1);
    return 0;
} 

运行截图

alt

2.互斥

临界资源竞争问题

加锁的形式对争抢到的资源进行互斥访问保护

1.互斥锁 函数

pthread_mutex_init

函数的功能:

​ 初始化线程锁

函数使用需要的头文件:

​ #include <pthread.h>

函数原型:

int  pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutexattr_t *attr)  

函数参数说明:

​ 1.mutex:线程锁变量指针

​ 2.attr:线程锁属性的结构体变量指针,通常使用默 认属性,传NULL

函数返回值说明:

​ 成功:返回0

​ 失败:返回-1,并且设置全局错误号

pthread_lock

函数的功能:

​ 线程锁加锁

函数使用需要的头文件:

​ #include <pthread.h> 函数原型:

int pthread_mutex_lock (pthread_mutex_t *mutex);

函数参数说明:

​ 1.mutex: 线程锁变量指针

函数返回值说明:

​ 成功:返回 0

​ 失败:返回 - 1, 并且设置全局错误号

pthread_unlock

函数的功能:

​ 线程锁解锁

函数使用需要的头文件: #include <pthread.h> 函数原型: int pthread_mutex_unlock(pthread_mutex_t *mutex) 函数参数说明: 1.mutex:线程锁变量指针 函数返回值说明: 成功:返回0 失败:返回-1,并且设置全局错误号

三、进程间通信(难点)

1.管道通信

(1)无名管道pipe(半双工通信)

只能用于亲缘间的进程通信

管道如果一直写入会陷入阻塞状态

如果管道关闭之后,继续往管道写入数据会触发SIGPIPE信号(管道破裂,进程强制结束)

alt

pipe函数(管道函数)

函数的功能:

​ 创建无名管道 函数使用需要的头文件: ​ #include <unistd.h> 函数原型: ​ int pipe(int pipefd[2]); 函数参数说明: ​ 1.pipefd:用于接收无名管道的文件描述符的数组地址 函数返回值说明: ​ 成功:返回0 ​ 失败:返回-1,并且设置全局错误号

单工:只能一方发送一方接收(收发方固定)

半双工:双方都可以收和发,但是同一时间只能一方发送一方接收

全双工:双方可以同时收发

(2)有名管道fifo

可以非亲缘间进程通信

有名管道主要借助Linux系统下的管道文件进行通信

mkfifo命令:shell命令效果,创建有名管道

使用方法:mkfifo 有名管道文件名

mkfifo函数

函数的功能:

​ 创建有名管道 函数使用需要的头文件: ​ #include <sys/types.h> ​ #include <sys/stat.h> 函数原型: ​ int mkfifo(const char *pathname, mode_t mode); 函数参数说明: ​ 1.pathname:有名管道的文件路径 ​ 2.mode:有名管道的文件的权限说明 函数返回值说明: ​ 成功:返回0 ​ 失败:返回-1,并且设置全局错误号

1.1作业

(1)完成简单的聊天窗口

聊天客户端1:fifo_client1.c

/*===============================================
*   文件名称:fifo_client1.c
*   创 建 者:青木莲华
*   创建日期:2025年08月11日
*   描    述:简单聊天窗实现(客户端1)
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{  
    pid_t pid = fork();
    
    char buf[1024] = {0};
    int ret = -1;
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    else if(0 == pid)           //子进程 读取C2发来的信息
    {

        int fd_read = open("./f2",O_RDONLY);    //接收客户端2的信息
        while(1)
        {   
            memset(buf,0,1024);
            ret = read(fd_read,buf,1024);
            if(ret == 0)
            {
                printf("The C2 has exit!\n");
                exit(0);
            }
            if(ret == -1)
            {
                perror("C1_read");
                exit(0);
            }
            printf("C2:%s",buf);
        }
    }
    else if(0 < pid)            //父进程 将C1发送信息写入FIFO管道
    {

        int fd_write = open("./f1",O_WRONLY);   //发送客户端1信息到客户端2
        printf("----------Client_1----------\n");
        while(1)
        {
            memset(buf,0,1024);
            fgets(buf,1024,stdin);  //获取输入
            ret = write(fd_write,buf,strlen(buf));
            if(ret == -1)
            {
                perror("C1_write");
                exit(0);
            }
        }
    }
    return 0;
} 

聊天客户端2:fifo_client2.c

/*===============================================
*   文件名称:fifo_client2.c
*   创 建 者:青木莲华
*   创建日期:2025年08月11日
*   描    述:简单聊天窗实现(客户端2)
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{ 
    
    pid_t pid = fork();
    
    char buf[1024] = {0};
    int ret;
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    else if(0 == pid)           //子进程 读取C1发来的信息
    {
        int fd_read = open("./f1",O_RDONLY);    //接收客户端1的信息
        while(1)
        {   
            memset(buf,0,1024);
            ret = read(fd_read,buf,1024);
            if(ret == 0)
            {
                printf("The C1 has exit!\n");
                exit(0);
            }
            if(ret == -1)
            {
                perror("C2_read");
                exit(0);
            }
            printf("C1:%s",buf);
        }
    }
    else if(0 < pid)            //父进程 将C2发送信息写入FIFO管道
    {
        
        int fd_write = open("./f2",O_WRONLY);   //发送客户端2信息到客户端1
        printf("----------Client_2----------\n");
        while(1)
        {
            memset(buf,0,1024);
            fgets(buf,1024,stdin);  //获取输入
            ret = write(fd_write,buf,strlen(buf));
            if(ret == -1)
            {
                perror("C2_write");
                exit(0);
            }
        }
    }
    return 0;
} 

运行结果

alt

(2)如何实现多个线程严格按顺序运行

/*===============================================
*   文件名称:static_order.c
*   创 建 者:青木莲华
*   创建日期:2025年08月11日
*   描    述:同步实现多进程执行顺序同步
================================================*/
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>



//同步信号量1  同步线程1(V)和线程2(P)
sem_t s1;
//同步信号量2  同步线程2(V)和线程3(P)
sem_t s2;
//同步信号量3  同步线程3(V)和线程1(P)
sem_t s3;
//线程1
void *func1(void *arg)
{
    while(1)
    {
            sem_wait(&s3); 
            printf("线程1:\n");
            printf("        sem3---P操作\n");
            printf("        sem1---V操作\n");
            sem_post(&s1);   
    }
}
//线程2
void *func2(void *arg)
{
    while(1)
    {
        sem_wait(&s1);
        printf("线程2:\n");
        printf("        sem1---P操作\n");
        printf("        sem2---V操作\n");
        sem_post(&s2);
    }
}
//线程3
void *func3(void *arg)
{
    while(1)
    {

        sem_wait(&s2);
        printf("线程3:\n");
        printf("        sem2---P操作\n");
        printf("        sem3---V操作\n");
        sem_post(&s3);
    }
}

int main(int argc, char *argv[])
{ 
    //初始化信号量
    //第二个参数设置为0表示线程间同步
    sem_init(&s1,0,0);
    sem_init(&s2,0,0);
    sem_init(&s3,0,1);
    //线程1
    pthread_t tid1;
    pthread_create(&tid1,NULL,func1,NULL);
    //线程2
    pthread_t tid2;
    pthread_create(&tid2,NULL,func2,NULL);
    //线程3
    pthread_t tid3;
    pthread_create(&tid3,NULL,func3,NULL);

    while(1);
    return 0;
} 

运行截图

alt

(3)完成群聊效果

Client 代码之一

/*===============================================
*   文件名称:Client_1.c
*   创 建 者:青木莲华
*   创建日期:2025年08月11日
*   描    述:(基于管道)群聊-客户端1
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>

int main(int argc, char *argv[])
{ 
    pid_t pid = fork();
    char buf[1024] = {0};
    char send[1030] = {0};
    int ret = -1;
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    else if(0 == pid)               //子进程 向服务器接收数据
    {
        int fd_read = open("./C1_1",O_RDONLY);
        if(-1 == fd_read)
        {
            perror("C1:fd_read");
            exit(0);
        }
        while(1)
        {        
            memset(buf,0,1024);
            ret = read(fd_read,buf,1024);
            if(-1 == ret)
            {
                perror("C1:read");
                exit(0);
            }   
            printf("%s",buf);
        }
    }
    else if(0 < pid)                //父进程 向服务器发送数据
    {
        int fd_write = open("./C1_2",O_WRONLY);
        if(-1 == fd_write)
        {
            perror("C1:write");
            exit(0);
        }
        while(1)
        {
            fgets(buf,1024,stdin);
            sprintf(send,"C1:%s",buf);
            ret = write(fd_write,send,strlen(send));
            if(-1 == ret)
            {
                perror("C1:write");
                exit(0);
            }
        } 
    }
    while(1);
    return 0;
} 

server.c

/*===============================================
*   文件名称:server.c
*   创 建 者:青木莲华
*   创建日期:2025年08月11日
*   描    述:(管道实现)群聊-服务器端
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

//C1信号量
sem_t s1;
//C2信号量
sem_t s2;
//C3信号量
sem_t s3;

//接收缓冲区
char buf_read[1024] = {0};
char buf_write[1024] = {0};


//Server获取C1信息
void *Client_1_read(void *arg)
{
    int c1_read = open("./C1_2",O_RDONLY);
    if(-1 == c1_read)
    {
        perror("c1_fd_read");
        exit(0);
    }
    int ret = -1;
    printf("C1_read is connection!\n");
    while(1)
    {
        ret = read(c1_read,buf_read,1024);
        if(-1 == ret)
        {
            perror("c1_read");
            exit(0);
        }
    
        memset(buf_write,0,1024);
        strcpy(buf_write,buf_read);
        sem_post(&s2);             //接收到C1 通过信息量 来控制发送给C2 C3
        sem_post(&s3);
    }
}

//Server发送信息到C1
void *Client_1_write(void *arg)
{
    int c1_write = open("./C1_1",O_WRONLY);
    if(-1 == c1_write)
    {
        perror("c1_fd_write");
        exit(0);
    }
    int ret = -1;
    printf("C1_write is connection!\n");
    while(1)
    {
        sem_wait(&s1);                  //等待V操作
        ret = write(c1_write,buf_write,strlen(buf_write));
        if(-1 == ret)
        {
            perror("c1_write");
            exit(0);
        }
    }
    
}
//Server获取C2信息
void *Client_2_read(void *arg)
{
    int c2_read = open("./C2_2",O_RDONLY);
    if(-1 == c2_read)
    {
        perror("c2_fd_read");
        exit(0);
    }
    int ret = -1;
    printf("C2_read is connection!\n");
    while(1)
    {
        ret = read(c2_read,buf_read,1024);
        if(-1 == ret)
        {
            perror("c2_read");
            exit(0);
        }
        memset(buf_write,0,1024);
        strcpy(buf_write,buf_read);
        sem_post(&s1);             
        sem_post(&s3);
    }
}

//Server发送信息到C2
void *Client_2_write(void *arg)
{
    int c2_write = open("./C2_1",O_WRONLY);
    if(-1 == c2_write)
    {
        perror("c2_fd_write");
        exit(0);
    }
    int ret = -1; 
    printf("C2_write is connection!\n");
    while(1)
    {
        sem_wait(&s2);                  //等待V操作
        ret = write(c2_write,buf_write,strlen(buf_write));
        if(-1 == ret)
        {
            perror("c2_write");
            exit(0);
        }
    }
    
}

//Server获取C3信息
void *Client_3_read(void *arg)
{
    int c3_read = open("./C3_2",O_RDONLY);
    if(-1 == c3_read)
    {
        perror("c3_fd_read");
        exit(0);
    }
    int ret = -1;
    printf("C3_read is connection!\n");
    while(1)
    {
        ret = read(c3_read,buf_read,1024);
        if(-1 == ret)
        {
            perror("c3_read");
            exit(0);
        }
        memset(buf_write,0,1024);
        strcpy(buf_write,buf_read);
        sem_post(&s1);
        sem_post(&s2);
    }
}

//Server发送信息到C3
void *Client_3_write(void *arg)
{
    int c3_write = open("./C3_1",O_WRONLY);
    if(-1 == c3_write)
    {
        perror("c3_fd_write");
        exit(0);
    }
    int ret = -1;
    printf("C3_write is connection!\n");
    while(1)
    {
        sem_wait(&s3);                  //等待V操作
        ret = write(c3_write,buf_write,strlen(buf_write));
        if(-1 == ret)
        {
            perror("c3_write");
            exit(0);
        }
    }
    
}



int main(int argc, char *argv[])
{ 
    //C1信号量初始化
    sem_init(&s1,0,0);
    //C2信号量初始化
    sem_init(&s2,0,0);
    //C3信号量初始化
    sem_init(&s3,0,0);
    
    //C1读线程
    pthread_t pt1_r;
    //C1写线程
    pthread_t pt1_w;
    //C2
    pthread_t pt2_r;
    pthread_t pt2_w;
    //C3
    pthread_t pt3_r;
    pthread_t pt3_w;

    pthread_create(&pt1_r,NULL,Client_1_read,NULL);
    pthread_create(&pt2_r,NULL,Client_2_read,NULL);
    pthread_create(&pt3_r,NULL,Client_3_read,NULL);

    pthread_create(&pt1_w,NULL,Client_1_write,NULL);
    pthread_create(&pt2_w,NULL,Client_2_write,NULL);
    pthread_create(&pt3_w,NULL,Client_3_write,NULL);



    while(1);
    return 0;
} 

运行截图

alt