线程的使用2

3. 利用管道实现互相的发收通信

jack.c

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

//有名管道进程间通信

void* read_thread(void* argc){
    //第二根管子
    if(access("/home/lsf/jincheng_course/fifo2",F_OK)==-1){//fifo2不存在则创建管道
         //创建一个有名管道
        int ret_fifo2 = mkfifo("/home/lsf/jincheng_course/fifo2",0777);
        if(ret_fifo2==-1){
            perror("file exist");
        }
    }
    //打开文件
    int fd2 = open("/home/lsf/jincheng_course/fifo2",O_RDONLY);
    if(fd2==-1){
        perror("open myfifo failed");
        return NULL;
    }

    char msg[128];
    //从管道2读数据
    while(1){
        memset(msg,0,sizeof(msg));
        read(fd2,msg,sizeof(msg)-1);
        printf("[rose说]:%s\n",msg);

        if(strcmp(msg,"bye\n")==0){
            break;
        }
    }
    close(fd2);
}

int main()
{

    //创建一个线程用于从管子2中读取数据
    pthread_t  tid1;
    pthread_create(&tid1,NULL,&read_thread,NULL);

    //access检测文件是否存在
    if(access("/home/lsf/jincheng_course/fifo1",F_OK)==-1){//fifo1不存在则创建管道
         //创建一个有名管道
        int ret_fifo1 = mkfifo("/home/lsf/jincheng_course/fifo1",0777);
        if(ret_fifo1==-1){
            perror("file exist");
        }
    }
    //打开文件
    int fd1 = open("/home/lsf/jincheng_course/fifo1",O_WRONLY);
    if(fd1==-1){
        perror("open myfifo failed");
        return -1;
    }
    

    char buf[128];//定义缓冲区
    //写文件
    while(1){

        memset(buf,0,sizeof(buf));
        fgets(buf,sizeof(buf),stdin);//获取键盘输入

        write(fd1,buf,sizeof(buf)-1);

        if(strcmp(buf,"bye\n")==0){//因为我们输入完bye后还会输入一个回车会被fgets读取到,所以为了避免错我们加一个回车
            break;
        }

    }
    close(fd1);

    return 0;
}

rose.c

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

//有名管道进程间通信

void* write_thread(void* argc){
    //第二根管子
    if(access("/home/lsf/jincheng_course/fifo2",F_OK)==-1){//fifo2不存在则创建管道
         //创建一个有名管道
        int ret_fifo2 = mkfifo("/home/lsf/jincheng_course/fifo2",0777);
        if(ret_fifo2==-1){
            perror("file exist");
        }
    }
    //打开文件
    int fd2 = open("/home/lsf/jincheng_course/fifo2",O_WRONLY);
    if(fd2==-1){
        perror("open myfifo failed");
        return NULL;
    }

    char msg[128];
    //往管道2写数据
    while(1){
        memset(msg,0,sizeof(msg));
        fgets(msg,sizeof(msg),stdin);//键盘输入
        write(fd2,msg,strlen(msg));

        if(strcmp(msg,"bye\n")==0){//因为我们输入完bye后还会输入一个回车会被fgets读取到,所以为了避免错我们加一个回车
            break;
        }

    }
    close(fd2);

}

int main()
{

    //创建一个线程用于从管子2中读取数据
    pthread_t  tid1;
    pthread_create(&tid1,NULL,&write_thread,NULL);

    //access检测文件是否存在,F_OK,R_OK,W_OK,X_OK
    if(access("/home/lsf/jincheng_course/fifo1",F_OK)==-1){//fifo1不存在则创建管道
         //创建一个有名管道
        int ret_fifo1 = mkfifo("/home/lsf/jincheng_course/fifo1",0777);
        if(ret_fifo1==-1){
            perror("file exist");
        }
    }
    //打开文件
    int fd1 = open("/home/lsf/jincheng_course/fifo1",O_RDONLY);
    if(fd1==-1){
        perror("open myfifo failed");
        return -1;
    }

    char buf[128];
    //读文件
    while(1){
        memset(buf,0,sizeof(buf));//清空
        read(fd1,buf,sizeof(buf)-1);

        printf("[jack说]:%s\n",buf);

        if(strcmp(buf,"bye\n")==0){
            break;
        }

    }
    
    close(fd1);

    return 0;
}

4. POSIX无名信号量

4.1 使用

如果我们要解决的是一个进程内部的线程间的同步互斥,那么也许不需要使用有名信号

量,因为这些线程共享同一个内存空间,我们可以定义更加轻量化的、基于内存的 (不在任 何文件系统内部) 无名信号量来达到目的。

这种信号量的使用步骤是:

1,在这些线程都能访问到的区域定义这种变量 (比如全局变量) ,类型是sem_t。 2,在任何线程使用它之前,用 sem_init( )初始化他。

3,使用 sem_wait( )/sem_trywait( )和 sem_post( )来分别进行 P 、V 操作。 4,不再需要时,使用 sem_destroy( )来销毁他。

无名信号量一般用在进程内的线程间,因此 pshared 参数一般都为 0。当将此种信号量 用在进程间时,必须将他定义在各个进程都能访问的地方—— 比如共享内存之中。

对于我们接触过的三种信号量:system-V 信号量和 POSIX 信号量 (named-sem 和 unnamed-sem) ,下面是他们的区别:

1,sys-V 信号量较古老,语法艰涩。POSIX 信号量简单,轻量。

2,sys-V 信号量可以对代表多种资源的多个信号量元素同一时间进行原子性的 P/V 操作,POSIX 信号量每次只能操作一个信号量。

3 ,sys-V 信号量和 named-sem 是系统范围的资源 ,进程消失之后继续存在 ,而 unnamed-sem 是进程范围的资源,随着进程的退出而消失。

4,sys-V 信号量的 P/V 操作可以对信号量元素加减大于 1 的数值,而 POSIX 信号量每 次 P/V 操作都是加减 1。

5 ,sys-V 信号量甚至还支持撤销操作——一个进程对 sys-V 信号量进行 P/V 操作时可

以给该操作贴上需要撤销的标识,那么当进程退出之后,系统会自动撤销那些做了标识的操 作。而 POSIX 信号没有此功能。

6,sys-V 信号量和 named-sem 适合用在进程间同步互斥,而 unamed-sem 适合用在线 程间同步互斥。

总的来说,system-V 的信号量功能强大,强大到臃肿啰嗦,如果在现实工作中不需要 那些高级功能,建议使用接口清晰、逻辑简单的 POSIX 信号量。

posix_no_name.c

#include<sys/fcntl.h>
#include<sys/stat.h>
#include<pthread.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <semaphore.h>


//posix无名信号量的使用,主线程输入字符串,子线程打印个数
sem_t space;
sem_t data;

//统计buf个数的线程
void* count(void* argc){
    char* str = (char*)argc;
    while(1){

        sem_wait(&data);
        printf("你输入的字符串个数为:%ld\n",strlen(str)-1);
        sem_post(&space);
        
    }

}

int main()
{

    //初始化无名信号量
    sem_init(&space,0,1);
    sem_init(&data,0,0);

    char buf[128];
    pthread_t tid;
    //创建子线程
    pthread_create(&tid,NULL,&count,(void*)buf);
    
    while(1){

        sem_wait(&space);//p -1操作
        memset(buf,0,sizeof(buf));
        //获取键盘输入
        printf("请输入字符串:");
        fgets(buf,sizeof(buf),stdin);
        sem_post(&data);//v +1操作
        
    }

    return 0;
}

5. 互斥锁与读写锁

如果信号量的值最多为 1,那实际上相当于一个共享资源在任意时刻最多只能有一个线 程在访问,这样的逻辑被称为“互斥”。这时,有一种更加方便和语义更加准确的工具来满 足这种逻辑,他就是互斥锁。

“锁”是一种非常形象的说法:就像一个房间只能住一个人一样,任何人进去之后就把 门锁上了,其他任何人都不能进去,直到进去的那个人重新开开锁,即释放了这个锁资源为 止:

5.1 互斥锁

pthread_mutex_lock(&m);//上锁

pthread_mutex_unlock(&m);//解锁

对互斥锁的操作无非就是:初始化、加锁、解锁、销毁。下面的代码通过展示两条线程 如何使用互斥锁来互斥地访问标准输出,来理解互斥锁的正确使用

huchi_lock.c

#include<sys/fcntl.h>
#include<sys/stat.h>
#include<pthread.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <semaphore.h>


//互斥锁的使用

pthread_mutex_t m;//定义互斥锁

void* count(void* argc){
    char* str = (char*)argc;
    pthread_mutex_lock(&m);
    while(*str!='\0'){

        fprintf(stderr,"%c",*str);
        // printf("%c",*str);
        str++;
        
    }
    printf("\n");
    pthread_mutex_unlock(&m);

}

int main()
{

    pthread_t tid;
    pthread_t tid2;
    //创建子线程
    pthread_create(&tid,NULL,&count,"hello");
    pthread_create(&tid2,NULL,&count,"world");

    pthread_exit(NULL);

    return 0;
}

5.2 压栈、或弹栈线程的取消处理例程

由于线程任何时刻都有可能持有诸如互斥锁、信号量等资源,一旦被取消很有可能导致

别的线程出现死锁,因此如果一条线程的确可能被取消,那么在被取消之前必须使用以下 API 来为将来可能出现的取消请求注册“处理例程”,让这些例程自动释放持有的资源。

lock_and_threadCancel.c

#include<sys/fcntl.h>
#include<sys/stat.h>
#include<pthread.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <semaphore.h>


//演示当一个线程被取消了,留下来的锁该怎么办

pthread_mutex_t m;//定义互斥锁

//压栈、或弹栈线程的取消处理例程
void routine(void* arg){
    pthread_mutex_unlock(&m);//解锁
}

void* count(void* argc){
    char* str = (char*)argc;
    pthread_mutex_lock(&m);//上锁

    //这一个步就是将解锁锁备份,防止线程被意外取消后产生错误(因为线程没了,他身上的锁肯定也跟着没了,那就找不到这把锁,也就没办法解锁了)
    pthread_cleanup_push(&routine,NULL);

    while(*str!='\0'){

        fprintf(stderr,"%c",*str);
        // printf("%c",*str);
        str++;
        
    }
    printf("\n");
    pthread_mutex_unlock(&m);//解锁
    pthread_cleanup_pop(1);//1代表执行备份操作,0代表不执行

}


int main()
{
    pthread_mutex_init(&m, NULL);//初始化互斥锁

    pthread_t tid;
    pthread_t tid2;
    //创建子线程
    pthread_create(&tid,NULL,&count,"hello");
    pthread_create(&tid2,NULL,&count,"world");

    pthread_exit(NULL);
    pthread_mutex_destroy(&m);//销毁锁

    return 0;
}

5.3 读写锁

互斥锁使用非常简便,但他也有不适用的场合——假如要保护的共享资源在绝大多数的 情况下是读操作,就会导致这些本可以一起读的线程阻塞在互斥锁上,资源得不到最大的利 用。

互斥锁的低效率,是因为没有更加细致地区分如何访问共享资源,一刀切地在任何时候 都只允许一条线程访问共享资源,而事实情况是读操作可以同时进行,只有写操作才需要互 斥,因此如果能根据访问的目的——读或者写,来分别加读锁 (可以重复加) 或者写锁 (只 允许一次一个) ,就能就能极大地提高效率 (尤其是存在大量读操作的情况下) 。

相关函数

pthread_rwlock_t rwlock; //定义读写锁

pthread_rwlock_init(&rwlock,NULL);  //初始化

pthread_rwlock_wrlock(&rwlock);    //上写锁

pthread_rwlock_rdlock(&rwlock);   //上读锁

pthread_rwlock_unlock(&rwlock);   //释放读写锁

pthread_rwlock_destroy(&rwlock);  // 销毁读写锁

rwlock.c

#include<sys/fcntl.h>
#include<sys/stat.h>
#include<pthread.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>

//演示读写锁
static pthread_rwlock_t rwlock;//定义读写锁
int global = 0;
void *routine1(void *arg){

    // 对共享资源进行写操作之前,必须加写锁 (互斥锁)
    pthread_rwlock_wrlock(&rwlock);

    global += 1;

    printf("I am %s, now global=%d\n", (char *)arg, global); 

    // 访问完之后释放该锁
    pthread_rwlock_unlock(&rwlock); 
    pthread_exit(NULL);
}

void *routine2(void *arg)
{
    // 对共享资源进行写操作之前,必须加写锁 (互斥锁)
    pthread_rwlock_wrlock(&rwlock);

    global   = 100;

    printf("I am %s, now global=%d\n", (char *)arg, global);

    // 访问完之后释放该锁
    pthread_rwlock_unlock(&rwlock);
    pthread_exit(NULL);
}

void *routine3(void *arg)
{
    // 对共享资源进行读操作之前,可以加读锁 (共享锁)
    pthread_rwlock_rdlock(&rwlock);

    printf("I am %s, now global=%d\n", (char *)arg, global);

    // 访问完之后释放该锁
    pthread_rwlock_unlock(&rwlock);
    pthread_exit(NULL);
}


int main (int argc, char *argv[])
{
    //对读写锁进行初始化
    pthread_rwlock_init(&rwlock,NULL);

    // 创建三条线程,对共享资源同时进行读写操作
    pthread_t t1, t2, t3;
    pthread_create(&t1, NULL, routine1, "thread 1");
    pthread_create(&t2, NULL, routine2, "thread 2");
    pthread_create(&t3, NULL, routine3, "thread 3");

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_join(t3, NULL);

    // 销毁读写锁
    pthread_rwlock_destroy(&rwlock);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/215615.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深圳招聘一般在哪个网站

深圳吉鹿力招聘网是一个专注于深圳招聘的平台&#xff0c;主要提供人才招聘服务。在深圳吉鹿力招聘网上&#xff0c;你可以找到各种深圳招聘信息&#xff0c;包括企业招聘、职位发布、简历投递等。深圳吉鹿力招聘网的出现&#xff0c;方便了求职者的投递和查询工作机会&#xf…

2023年江西省“振兴杯”网络信息行业职业技能竞赛 Web4 Writeup

这次振兴杯碰到的一道题&#xff0c;某些姿势之前貌似没有碰过&#xff0c;简单记一下吧 源码 <?php class Bird{public $funcs;public $salt;public $flag;function say_flag(){$secret hash_hmac(sha256, $_GET[salt], file_get_contents(/flag));$hmac hash_hmac(sha…

开源数据大屏系统介绍

睿思BI数据大屏系统现已开源&#xff0c;通过拖拽配置的方式构建大屏&#xff0c;支持零代码开发。并且包含大量大屏模版&#xff0c;方便用户快速创建大屏应用。 系统主要包括数据准备、大屏设计、权限管理3个部分内容。 1.数据准备 1.1 创建数据源&#xff1a;定义BI系统链…

PHP使用HTTP代码示例模板

PHP是一种广泛用于服务器端的编程语言&#xff0c;它提供了许多内置的函数和扩展&#xff0c;以便开发人员能够轻松地处理HTTP请求和响应。在PHP中&#xff0c;您可以使用以下代码示例模板来处理HTTP请求和生成HTTP响应。 php复制代码 <?php // 处理GET请求 if ($…

计算机组成学习-存储系统总结

复习本章时&#xff0c;思考以下问题&#xff1a; 1)存储器的层次结构主要体现在何处&#xff1f;为何要分这些层次&#xff1f;计算机如何管理这些层次&#xff1f;2)存取周期和存取时间有何区别&#xff1f;3)在虚拟存储器中&#xff0c;页面是设置得大一些好还是设置得小一…

园区无线覆盖方案(智慧园区综合解决方案)

​ 李经理正苦恼头疼的工业园区数字化改造项目。近年企业快速增长,园区内Argent工业设备激增,IT部门应接不暇。为确保生产系统稳定运行,IT管理团队经过反复摸索,决定进行全面的数字化升级。然而改造之艰巨远超想象——混杂的接入环境、复杂的专线部署、长达数月的建设周期,种种…

法律情境扮演、逆向推理文字游戏、AIGC创作……见证AI极致生产力!

飞桨星河社区&#xff0c;以飞桨和文心大模型为核心&#xff0c;集开放数据、开源算法、云端GPU算力及大模型开发工具于一体&#xff0c;在大模型范式下&#xff0c;为开发者提供模型与应用的高效开发环境。在成立的5年以来&#xff0c;已汇集660万AI开发者&#xff0c;覆盖深度…

LLM之RAG实战(一):使用Mistral-7b, LangChain, ChromaDB搭建自己的WEB聊天界面

一、RAG介绍 如何使用没有被LLM训练过的数据来提高LLM性能&#xff1f;检索增强生成&#xff08;RAG&#xff09;是未来的发展方向&#xff0c;下面将解释一下它的含义和实际工作原理。 ​ 假设您有自己的数据集&#xff0c;例如来自公司的文本文档。如何让ChatGPT和其他…

中级工程师评审条件:如何成为一名合格的中级工程师

作为一名工程师&#xff0c;不仅需要具备扎实的技术基础和实践能力&#xff0c;还需要通过评审来证明自己的能力水平。在成为一名合格的中级工程师之前&#xff0c;你需要满足一系列评审条件。甘建二今天将详细介绍中级工程师评审的要求和标准&#xff0c;帮助你成为更优秀的工…

基于java技术的电子商务支撑平台

摘 要 随着网络技术的发展&#xff0c;Internet变成了一种处理日常事务的交互式的环境。互联网上开展各种服务已经成为许多企业和部门的急切需求。Web的普遍使用从根本上改变了人们的生活方式、工作方式&#xff0c;也改变了企业的经营方式和服务方式。人们可以足不出户办理各…

BLIP和BLIP2

1.BLIP BLIP的第一个共享是将图像文本理解与图像文本生成任务进行了统一&#xff0c;形成了多模态统一模型&#xff0c;模型在ITC任务上的效果也比CLIP更好。 1.1任务 ITC&#xff1a;就是CLIP中的图像文本对比学习任务 ITM&#xff1a;针对ITC任务中匹配不正确的样本&#…

大数据|计算机毕业设计——基于Django协同过滤算法的房源可视化分析推荐系统的设计与实现

大数据|计算机毕业设计——基于Django协同过滤算法的房源可视化分析推荐系统的设计与实现 技术栈&#xff1a;大数据爬虫/机器学习学习算法/数据分析与挖掘/大数据可视化/Django框架/Mysql数据库 本项目基于 Django框架开发的房屋可视化分析推荐系统。这个系统结合了大数据爬…

3D旋转tab图

上图 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>3D旋转tab图</title><style>* {margin: 0;padding: 0;}body {height: 100vh;background: linear-gradient(to top, #29323c, #…

办公室状态公告系统保护涉密和高级领导办公室

天纵办公室状态公告系统通过在办公室外设置一个公告屏的方式告诉来访者办公室目前的使用状态&#xff0c;以防来访者盲目敲门影响办公室内领导的工作。主要用于高级领导办公室、涉密办公室等重要场所。 天纵办公室状态公告系统可能通过触摸电脑、平板或机械按钮等方式控制室外公…

有哪些电话销售的经验值得分享?

有哪些电话销售的经验值得分享&#xff1f; 电话销售&#xff0c;这是一项挑战与机遇并存的行业。它不仅要求你具备良好的沟通技巧和专业知识&#xff0c;更需要你有足够的耐心和热情。以下是一些值得分享的电话销售经验&#xff0c;帮助你更好地开展销售工作。 1. 精心准备。…

深度学习——第3章 Python程序设计语言(3.3 Python数据类型)

3.3 Python数据类型 目录 1. Python数值数据类型 2. Python库的导入和使用 3. Python序列数据类型 4. Python组合数据类型 计算机能处理各种类型的数据&#xff0c;包括数值、文本等&#xff0c;不同的数据属于不同的数据类型&#xff0c;有不同的存储方式&#xff0c;支持…

AndroidStudio - 新版本 Logcat 使用详解

最近这俩天正好有时间给自己做一下减法&#xff0c;忘记是去年还是今年&#xff0c;在升级 AndroidStudio 后使用 Logcat查看日志的方式也发生了一些变化&#xff0c;虽然一直在使用&#xff0c;但每当看到之前还未关闭 Logcat 命令行工具额昂也&#xff0c;就感觉可能还存在知…

广州找工作用什么软件

广州吉鹿力招聘网是一个很好的广州找工作网站&#xff0c;它提供了丰富的招聘信息&#xff0c;并且有专业的招聘团队负责筛选简历。广州吉鹿力招聘网在广州市的招聘市场上有着较高的知名度和影响力&#xff0c;用户可以通过广州吉鹿力招聘网了解最新的招聘信息&#xff0c;找到…

Android studio版本对用的gradle版本和插件版本(注意事项)

简介 Android Studio 构建系统以 Gradle 为基础&#xff0c;并且 Android Gradle 插件添加了几项专用于构建 Android 应用的功能。虽然 Android 插件通常会与 Android Studio 的更新步调保持一致&#xff0c;但插件&#xff08;以及 Gradle 系统的其余部分&#xff09;可独立于…

行业分析:2023年智能自动化药房市场现状及发展前景

医药电商是近些年的行业风口&#xff0c;尤其是随着大型互联网平台的介入和互联网医院的兴起&#xff0c;医药电商步入高速增长期。第三方交易服务平台在医药电商的销售额占比为58%&#xff0c;而到了2020年下降至40%。在终端销售额中&#xff0c;大型医院占据了59.7%的份额&am…