首页 > 系统相关 >IO进程----线程

IO进程----线程

时间:2025-01-19 13:27:40浏览次数:3  
标签:NULL return int ---- 线程 IO pthread include

什么是线程

概念

线程是一个轻量级的进程,为了提高系统的性能引入线程。

线程和进程是参与统一的调度。

在同一个进程中可以创建的多个线程, 共享进程资源。

(Linux里同样用task_struct来描述一个线程)

进程和线程的区别

相同点:

都为系统提供了并发执行的能力

不同点:

调度和资源:线程是系统调度的最小单位;进程是资源分配的最小单位。

地址空间方面:同一个进程创建的多个线程共享该进程的资源;进程的地址空间相互独立。

通信方面:线程通信相对简单,只需要通过全局变量可以实现,但是需要考虑临界资源访问的问题;进程通信比较复杂,需要借助进程间的通信机制(借助3g-4g内核空间)

安全性方面:线程安全性差一些,当进程结束时会导致所有线程退出;进程相对安全。

面试题:程序什么时候该使用线程?什么时候用进程?(深圳棱镜空间智能科技有限公司)(北京明朝万达)

1. 对资源的管理和保护要求高,不限制开销和效率时,使用多进程。

2. 要求效率高、速度快的高并发环境时,需要频繁创建、销毁或切换时,资源的保护管理要求不是很高时,使用多线程。

线程资源

共享的资源:可执行的指令、静态数据、进程中打开的文件描述符、信号处理函数、当前工作目录、用户ID、用户组ID

私有的资源:线程ID (TID)、PC(程序计数器)和相关寄存器、堆栈(局部变量, 返回地址)、错误号 (errno)、信号掩码和优先级、执行状态和属性

函数接口

创建线程:pthread_create

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 
                                void *(*start_routine) (void *), void *arg);
功能:创建线程
参数:      thread:线程标识
            attr:线程属性, NULL:代表设置默认属性
            start_routine:函数名:代表线程函数(自己写的)
            arg:用来给前面函数传参
返回值:成功:0
      失败:错误码
      
编译的时候需要加 -pthread 链接动态库

函数指针格式: 数据类型 (* 指针名)(参数列表);

#include <stdio.h>
#include <stdlib.h>
int test(int (*p)(int, int), int a, int b) //p=fun, a=3, b=4
{
    return p(a,b); //fun(3,4);
}
int fun(int n, int m)  //n=3, m=4
{
    return n * m;  //3*4=12
}

int main(int argc, char const *argv[])
{
      printf("%d\n", test(fun, 3, 4));  //12
      return 0;
}

例子:

#include <stdio.h>
#include <pthread.h>

void *handler_thread(void *arg)
{
    printf("in handler_thread\n");
    while (1)
        ; //不让线程退出
    return NULL;
}

int main(int argc, char const *argv[])
{
    pthread_t tid;
    if (pthread_create(&tid, NULL, handler_thread, NULL) != 0)  //创建线程
    {
        perror("phtread err");
        return -1;
    }
    printf("in main\n");
    while(1);  //让主线程不要结束
    return 0;
}

退出线程:pthread_exit

void  pthread_exit(void *value_ptr) 
功能:用于退出线程的执行
参数:value_ptr:线程退出时返回的值
#include <stdio.h>
#include <pthread.h>

void *handler_thread(void *arg)
{
    printf("in handler_thread\n");
    pthread_exit(NULL);   //退出当前线程
    while (1)
        ; //不让线程退出
    return NULL;
}

int main(int argc, char const *argv[])
{
    pthread_t tid;
    if (pthread_create(&tid, NULL, handler_thread, NULL) != 0) //创建线程
    {
        perror("phtread err");
        return -1;
    }
    printf("in main\n");
    while (1)
        ; //让主线程不要结束
    return 0;
}

回收线程资源

int  pthread_join(pthread_t thread,  void **value_ptr) 
功能:用于等待一个指定的线程结束,阻塞函数
参数:thread:创建的线程对象,线程ID
     value_ptr:指针*value_ptr 用于指向线程返回的参数, 一般为NULL
返回值:成功 : 0
       失败:errno

int pthread_detach(pthread_t thread);
功能:让线程结束时自动回收线程资源,让线程和主线程分离,非阻塞函数
参数:thread:线程ID
非阻塞式的,例如主线程分离(detach)了线程T2,
那么主线程不会阻塞在pthread_detach(),pthread_detach()会直接返回,
线程T2终止后会被操作系统自动回收资源
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *handler_thread(void *arg)
{
    printf("in handler_thread\n");
    sleep(2);
    pthread_exit(NULL); //退出当前线程
    while (1)
        ; //不让线程退出
    return NULL;
}

int main(int argc, char const *argv[])
{
    pthread_t tid;
    if (pthread_create(&tid, NULL, handler_thread, NULL) != 0) //创建线程
    {
        perror("phtread err");
        return -1;
    }

    // pthread_join(tid, NULL);   //阻塞等待指定的线程结束然后给其回收资源
    pthread_detach(tid);     //不阻塞,让指定线程结束时自动回收资源
    printf("in main\n");
    while (1)
        ; //让主线程不要结束
    return 0;
}
练习:通过父子进程完成对文件的拷贝(cp)

1. 通过父子进程完成对文件的拷贝(cp),父进程从文件开始到文件的一半开始拷贝,子进程从文件的一半到文件末尾。要求:文件IO cp src dest

(1) 文件长度获取:lseek

(2) 子进程定位到文件一半:lseek

(3) 父进程怎么准确读到文件一半的位置?

(4) fork之前打开文件,父子进程读写时,位置指针是同一个

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

int main(int argc, char const *argv[])
{
    int fd1, fd2;
    pid_t pid;
    char buf[32] = "";
    ssize_t n;
    if (argc != 3)
    {
        printf("err: %s <srcfile> <destfile>\n", argv[0]);
        return -1;
    }

    fd1 = open(argv[1], O_RDONLY);
    if (fd1 < 0)
    {
        perror("fd1 err");
        return -1;
    }
    fd2 = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0777);
    if (fd2 < 0)
    {
        perror("fd2 err");
        return -1;
    }

    //获取源文件长度的一半
    off_t len = lseek(fd1, 0, 2) / 2;

    //创建子进程
    pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0) //拷贝后半段
    {
        //定位到一半的位置
        lseek(fd1, len, 0);
        lseek(fd2, len, 0);

        //读源文件,写入目标文件
        while ((n = read(fd1, buf, 32)) > 0)
        {
            write(fd2, buf, n);
            sleep(1);
        }
    }
    else //拷贝前半段
    {
        wait(NULL); //等子进程读写完父进程再拷贝
        //定位到文件开头
        lseek(fd1, 0, 0);
        lseek(fd2, 0, 0);

        //读源文件,写入目标文件
        while (len > 0)
        {
            if (len > 32)
                n = read(fd1, buf, 32);
            else
                n = read(fd1, buf, len);
            write(fd2, buf, n);
            len -= n;   //len保存的是剩余要读的字符个数
            sleep(1);
        }
    }
    close(fd1);
    close(fd2);

    return 0;
}
练习:输入输出,quit结束

通过线程实现数据的交互,主线程循环从终端输入,线程函数将数据循环输出,当输入quit结束程序。

1) 全局变量进行通信

2) 加上标志位(flag),实现主线程输入一次,线程函数打印一次, int flag = 0;

#include <stdio.h>
#include <pthread.h>
#include <string.h>
char s[32];
int flag = 0; //为了进行线程间通讯,保证主线程先输入然后从线程再输出

void *handler_thread(void *arg)
{
    while (1)
    {
        if (flag == 1) //主线程输入完将flag置1从线程再输出
        {
            if (strcmp(s, "quit") == 0)
                break;
            printf("%s\n", s);
            flag--;
        }
    }
    return NULL;
}

int main(int argc, char const *argv[])
{
    pthread_t tid;
    if (pthread_create(&tid, NULL, handler_thread, NULL) != 0)
    {
        perror("err");
        return -1;
    }

    while (1)
    {
        //scanf前也可以不加if判断,利用阻塞时间让从线程输出
        // if (flag == 0)  //从线程输出完将flag置0主线程再输入
        // {
            scanf("%s", s);
            flag++;

            if (strcmp(s, "quit") == 0)
                break;
        // }
    }
}

标签:NULL,return,int,----,线程,IO,pthread,include
From: https://blog.csdn.net/Mumyi_/article/details/145242071

相关文章

  • view.cls = cls,这样做view.cls是实例变量还是类变量
    view.cls=cls这样的赋值操作将cls赋值给view对象的cls属性。是否是实例变量还是类变量,取决于view和cls的上下文。如果view是类的实例:这种情况中,view.cls是实例变量。这意味着你在view对象上设置了一个属性cls,它只会影响view实例的状态,而不会影响类本身。......
  • Spring Boot Admin服务监控
    目录AdminServerAdminClient添加认证SpringBootAdmin监控SpringCloud服务结合Eureka注册中心SpringBootAdmin是一个用于管理和监控SpringBoot应用程序的工具。它通过SpringBootAdminClient通过HTTP注册,或者使用SpringCloud如Eureka进行服务发现。其用户界面是......
  • leetcode——三数之和(java)
    给你一个整数数组nums,判断是否存在三元组[nums[i],nums[j],nums[k]]满足i!=j、i!=k且j!=k,同时还满足nums[i]+nums[j]+nums[k]==0。请你返回所有和为0且不重复的三元组。注意:答案中不可以包含重复的三元组。示例1:输入:nums=[-1,0,1,2,-1,-4]输......
  • 医疗肠胃内窥镜主机技术详解及实现路线(方案)
    医疗肠胃内窥镜主机技术详解及实现路线(基于MCU、FPGA与ARM处理器的实现)摘要肠胃内窥镜主机是现代消化系统疾病诊断与治疗中关键的电子控制与图像处理单元。随着微控制器单元(MCU)、现场可编程门阵列(FPGA)和高性能ARM处理器(如RK3588)的广泛应用,内窥镜主机在图像处理、光源控制和用......
  • 【字节青训营-1】:万字长文深入拆解Git核心组织结构与Git最佳实践
    本文为笔者参加字节青训营时听字节青训课所做的笔记。本文目录一、Git的相关方向二、工作中Git相关的点三、版本控制的发展历程3.1本地控制版本3.2集中式版本控制3.3分布式版本控制四、Git简单实战4.1gitinit4.2Git配置4.3GitAdd与Objects4.4refs4.5gitchec......
  • 电动智能汽车的未来
    我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师:简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身边有这样灵性的人,一定要......
  • 【Typora】2025最新Typora安装下载与破解免费使用保姆级图文教程
    本文目录一、下载Typora二、安装Typora三、使用Typora一、下载Typorahttps://www.typoraio.cn/首先我们去Typroa的官网下载Typora。这里可以使用中文站,不会太卡。二、安装Typora选定好自己的路径进行下载,这里推荐D盘进行下载。然后创建一个桌面版图标,方便下......
  • HTTP 常用方法解析
    一、HTTP协议简介HTTP,即超文本传输协议(HyperTextTransferProtocol),是互联网通信的基石,广泛应用于Web浏览器与服务器之间的数据交互。它构建起客户端与服务器沟通的桥梁,使得我们能够流畅地浏览网页、获取各类信息资源,从新闻资讯、社交媒体到在线购物、视频娱乐,几乎所有网络......
  • Windows图形界面(GUI)-QT-C/C++ - Qt QToolBox详解教程
    公开视频-> 链接点击跳转公开课程博客首页-> ​​​链接点击跳转博客主页目录QToolBox基础概述QToolBox简介使用场景QToolBox常见样式选项卡式界面页面内容动态管理页面QToolBox属性设置添加和删除页面页面标题页面索引QToolBox内容操作添加页面插入页面删......
  • SpringBoot体育场馆在线预约系统ig5br(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表用户,场地类别,场地信息,场地预约,取消预约,统计数据开题报告内容一、研究背景与意义随着人们健康意识的增强,体育锻炼已成为现代人日常生活的重要组成部分。体......