Linux系统编程 day10 接着线程(中期头大,还要写论文)

线程有点懵逼

线程之前函数回顾以及总结部分(对不清楚的问题再思考)

cpp 复制代码
线程控制原语                进程控制原语
pthread_create();          fork();
pthread_self();            getpid();
pthread_exit();            exit();
pthread_join();            wait()/waitpid();
pthread_cancel();          kill();
pthread_detach();

这里写一下昨天的join函数,那块有点难理解。

cpp 复制代码
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

struct thrd{
    int var ; 
    char str[256];
};


void* func(void * arg)
{
    struct thrd *th; // 这里不能直接使用结构体,因为这是在栈上的
    th = malloc(sizeof(struct thrd)); // 这边需要在堆区分配内存,如果不使用堆区会发生错误,因为当线程执行完之后,栈上的内存会释放,如果返回栈上的内容会非法访问。
    th->val = 200;
    strcpy(th->str , "hello pthread_join");
    return (void*)th;
}

int main(int argc , char* argv[])
{
    pthread_t tid ; 
    struct thrd *retval;
    int ret = pthread_create(&tid , NULL , func , NULL);
    if(ret != 0){
        fprintf(stderr , "pthread_create error : %s\n" , strerror(ret)); 
        exit(1);   
    }

    ret = pthread_join(tid , (void**)&retval);
    if(ret != 0){
        fprintf(stderr , "pthread_join error : %s\n" , strerror(ret)); 
        exit(1);   
    }

    printf("child thread exit with var = %d , str = %s\n " , retval->val , retval->str);

    pthread_exit(NULL);
    
}

线程当中用perror没有用,线程会直接返回errno,所以在线程中错误一般使用下面这个格式:

cpp 复制代码
if(ret != 0){
    fprintf(stderr , "pthread_create error : %s\n" , strerror(ret)); 
    exit(1);   
}

线程分离之后,子线程结束会自动回收,自动清理PCB,无需回收

cpp 复制代码
ret = pthread_detach(tid)

线程属性

初始化线程属性

cpp 复制代码
int pthread_attr_init(pthread_attr_t* attr);
成功返回0
失败errno

fprintf(stderr , "pthread_xxx error:%s\n" , strerror(ret));

销毁线程属性所占用的资源

cpp 复制代码
int pthread_attr_destroy(pthread_attr_t* attr);
成功返回0
失败errno
fprintf(strerr , "pthread_attr_destroy error:%s\n" , strerror(ret));

设置线程分离

cpp 复制代码
int pthread_attr_setdetachstate(pthread_attr_t* attr , int detachstate);

参数:attr:已初始化的线程属性
detachstate : PTHREAD_CREATE_DETACHED(分离线程)
               PTHRED_CREATE_JOINABLE(非分离线程)
cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>


void* tfn(void* arg){

    printf("thread:pid = %d , tid = %lu\n" , getpid() , pthread_self());
    return NULL;
}

int main(int argc , char *argv[])
{
    pthread_t tid ;
    //tid = pthread_self();
    pthread_attr_t attr;
    int ret = pthread_attr_init(&attr);
    if(ret != 0){
        fprintf(stderr , "pthread_attr_init error:%s/n" , strerror(ret));
        exit(1);
    }
    ret = pthread_attr_setdetachstate(&attr , PTHREAD_CREATE_DETACHED);//设置线>程属性为分离属性
    if(ret != 0){
        fprintf(stderr , "pthread_attr_setdetachstate error:%s/n" , strerror(ret));
        exit(1);
    }
    ret = pthread_create(&tid , &attr , tfn , NULL);
    if(ret != 0){
        fprintf(stderr , "pthread_create error:%s/n" , strerror(ret));
        exit(1);
    }
    ret = pthread_attr_destroy(&attr);
    if(ret != 0){
        fprintf(stderr , "pthread_attr_destory error:%s/n" , strerror(ret));
        exit(1);
    }

    ret = pthread_join(tid , NULL); // 如何查看是否是分离状态,不使用那个函数,>函数太长了,可以使用join函数,如果join函数回收失败出现错误,就表示已经为线程分离
。
    if(ret != 0){
        fprintf(stderr , "pthread_join error:%s/n" , strerror(ret));
        exit(1);
    }
    printf("main:pid = %d , tid = %lu\n" , getpid() , pthread_self());
    pthread_exit(NULL);
}

线程使用注意事项

1、主线程退出其他线程不退出,主线程要使用pthread_exit()。

2、避免僵尸线程。

3、malloc和mmap申请的内存可以被其他线程释放。

4、避免在多线程模型中使用fork,子进程中只有调用fork的线程存在,其余均被退出。

5、信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制。

线程同步

同步:协同,讲究先后次序。

协同步调,对公共区域数据按序访问,防止数据混乱,产生与时间有关的错误。

锁的使用

建议锁,对公共数据进行保护,所有进程应该在访问公共数据之前先拿锁再访问,但锁本身不具备强制性。

互斥锁/互斥量mutex

使用mutex(互斥量、互斥锁)一般步骤

cpp 复制代码
1、pthread_mutex_t lock;    创建锁
2、pthread_mutex_init();    初始化锁
3、pthread_mutex_lock();    加锁
4、访问共享数据
5、pthread_mutex_unlock();  解锁
6、pthread_mutex_destory(); 销毁锁

上述函数成功返回0,失败返回errno

注意事项:尽量保证锁的粒度

cpp 复制代码
restrict关键字:
用来限定指针变量,被该关键字限定的指针变量所指向的内存操作,必须由本指针完成

int pthread_mutex_init(pthread_mutex_t *restrict mutex , const pthread_mutexattr_t *restrict attr);

今天没学完,还要写论文

相关推荐
mqiqe5 分钟前
Minio Linux 安装 systemctl启动配置
linux·运维·服务器
Ant?15 分钟前
rk3588 驱动开发(二)第四章嵌入式 Linux LED 驱动开发实验
linux·运维·驱动开发
tyler-泰勒15 分钟前
Linux:权限相关问题
linux·运维·服务器
“αβ”34 分钟前
Linux的进程间通信
linux·运维·服务器
李菠菜35 分钟前
利用Nginx实现高性能的前端打点采集服务(支持GET和POST)
linux·前端·nginx
小诸葛的博客2 小时前
使用logrotate实现日志轮转
linux
JohnYan2 小时前
工作笔记-应用磁盘无感扩容
linux·后端·操作系统
RQ_ghylls2 小时前
21.disql命令登录达梦数据库,查询并操作数据库
java·linux·开发语言·数据库·windows·docker·数据分析
李菠菜2 小时前
非root用户运行Docker命令的最佳实践
linux·docker·容器
成工小白3 小时前
【Linux】冯诺依曼体系结构及操作系统架构图的具体剖析
linux·计算机外设