zoukankan      html  css  js  c++  java
  • 浅谈一下linux线程

      1.线程是进程中最小执行单元,多线程共享同一个进程的地址空间
      2.Linux 内核调度的对象是线程,所以一个进程中多个线程参与操作操作系统统一调度

      使用线程优点:
        <1>效率高
        <2>线程之间通信比较简单(全局变量)

    使用线程缺点:
      安全性差

    线程API

      1.线程创建
        int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
        void *(*start_routine) (void *), void *arg);

        参数:
          @thread 获取线程ID
          @attr 设置线程属性 NULL:默认属性
          @start_routine 线程执行的函数
          @arg 给线程执行函数传递的参数

    返回值:
      成功返回0, 失败返回错误码

    实例如下:

    #include <head.h>
    
    void *output(void *arg)
    {
        int data = *(int *)arg;
        printf("%d : %d
    ",pthread_self(),data);
        return;
    }
    
    int main(int argc, const char *argv[])
    {
        int ret;
        int count = 0;
        int data = 0;
        pthread_t tid;
    
        while(1)
        {
            /*bug :
             * 一个线程被创建后,不一定立即运行,而每个线程都是从data的地址中读取数据
             *,而data地址的数据又一直在发生变化,这样可能会导致,线程运行的时候,从data地址读取
             *的数据已经不是它想要的数据了。
             *
             */
            ret = pthread_create(&tid,NULL,output,&data);
            if(ret != 0){
                fprintf(stderr,"Fail to pthread_create : %s
    ",strerror(ret));
                exit(EXIT_FAILURE);
            }
            i ++;
    
            pthread_detach(tid);
            data ++;
            count ++;
            printf("count = %d!
    ",count);
        //    pthread_join(tid,NULL);
        }
        
        return 0;
    }

    运行如下

    实例如下
    创建两个子线程,子1线程输出data值,子2线程data ++操作,主线程将data -- 操作 (data 在主线程定义)

    #include <head.h>
    
    void *output_data(void *arg)
    {
        int data = *(int *)arg;
        
        while(1)
        {
            data = *(int *)arg;
            printf("data = %d!
    ",data);
    //        sleep(1);    
        }
    }
    
    void *add_data(void *arg)
    {
        int *pdata = (int *)arg;
        while(1)
        {
            (*pdata) ++;
        }
    }
    
    int main(int argc, const char *argv[])
    {
        int ret;
        pthread_t tid1,tid2;
        int data = 10;
    
        ret = pthread_create(&tid1,NULL,output_data,&data);
        if(ret != 0){
            fprintf(stderr,"Fail to pthread_create : %s
    ",strerror(ret));
            exit(EXIT_FAILURE);
        }
        
        ret = pthread_create(&tid2,NULL,add_data,&data);
        if(ret != 0){
            fprintf(stderr,"Fail to pthread_create : %s
    ",strerror(ret));
            exit(EXIT_FAILURE);
        }
    
        while(1)
        {
            data --;
        }
        
        exit(EXIT_SUCCESS);
    }

     运行结果

    可见线程的运行并没有先后顺序

    2.线程退出

    (1)线程函数返回 (return)
    (2)pthread_exit
    (3)pthread_cancel
    (4)进程结束,这个进程中所有的线程都退出

    void pthread_exit(void *retval);
    功能:用来退出一个线程
    参数:
    @retval 返回一个地址值


    int pthread_join(pthread_t thread, void **retval);
    功能:等待一个线程退出,将退出线程未释放的资源释放掉
    参数:
    @thread 需要等待退出线程的ID
    @retval 获得线程退出返回的值


    void *thread_function(void *arg)
    {
    int data = 100;

    pthread_exit(&data);
    }

    int main()
    {
    int *p;

    pthread_join(tid,&p);
    return 0;
    }

    3.将线程标记分离

    分离状态的线程在结束的时候,系统自动回收它的资源

    int pthread_detach(pthread_t thread);
    @thread 线程ID


    实例如下:
      (1)全局int data[] = {1,2,3,4,5,6,7,8,9,10,11};
      (2)线程1一直对全局数组做逆置
      (3)线程2一直对全局数组输出

    #include <head.h>
    
    //pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_t lock;
    
    int a[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
    
    void *output_array(void *arg)
    {
        int i = 0;
        
        //实现效果:如果发现逆置操作没有结束,则不输出此时阻塞自己
        while(1)
        {    
            pthread_mutex_lock(&lock);
            for(i = 0;i < 20;i ++){
                printf("%d ",a[i]);
            }
            printf("
    ");
            pthread_mutex_unlock(&lock);
        
        }
    }
    
    void *reverse_array(void *arg)
    {
        int t;
        int i ,j;
        
        //实现效果:如果输出操作没有结束,则不逆置阻塞字节
        while(1)
        {
            pthread_mutex_lock(&lock);
            i = 0;
            j = 19;
    
            while(i < j){
                t = a[i];
                a[i] = a[j];
                a[j] = t;
    
                i ++;
                j --;
            }
            pthread_mutex_unlock(&lock);
        }
    }
    
    int main(int argc, const char *argv[])
    {
        int ret;
        pthread_t tid1,tid2;
        int data = 10;
    
        ret = pthread_mutex_init(&lock,NULL);
        if(ret != 0){
            fprintf(stderr,"Fail to pthread_mutex_init");
            exit(EXIT_FAILURE);
        }
    
        ret = pthread_create(&tid1,NULL,output_array,NULL);
        if(ret != 0){
            fprintf(stderr,"Fail to pthread_create : %s
    ",strerror(ret));
            exit(EXIT_FAILURE);
        }
        
        ret = pthread_create(&tid2,NULL,reverse_array,NULL);
        if(ret != 0){
            fprintf(stderr,"Fail to pthread_create : %s
    ",strerror(ret));
            exit(EXIT_FAILURE);
        }
    
        pthread_join(tid1,NULL);
        
        exit(EXIT_SUCCESS);
    }

    运行结果

    并没有出现还没有逆置完成就输出的

    但如果去掉

    pthread_mutex_lock(&lock);

    pthread_mutex_unlock(&lock);

    呢?

     线程互斥锁

      功能:对共享资源实现互斥访问,保证访问的完整性

      如果使用互斥锁,每个线程在访问共享资源,都必须先获得互斥锁,然后在访问共享资源,如果无法获得互斥锁
      ,则表示有其他线程正在访问共享资源,此时没有获得锁的线程会阻塞,直到其他线程释放锁,能再次获得锁

    1.定义互斥锁[全局,让每个线程都可以访问到]
    pthread_mutex_t lock;

    2.初始化互斥锁

    //[线程创建之前]动态初始化,属性使用默认 NULL
    int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

    //静态初始化定义初始化
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

    3.获得互斥锁
    int pthread_mutex_lock(pthread_mutex_t *mutex);//操作共享资源之前加锁
    int pthread_mutex_trylock(pthread_mutex_t *mutex);

    4.释放锁
    int pthread_mutex_unlock(pthread_mutex_t *mutex);//操作共享资源结束的时候

    5.销毁锁
    int pthread_mutex_destroy(pthread_mutex_t *mutex);//不需要再次使用锁的时候

     线程间同步

      同步:相互之间配合完成一件事情
      互斥:保证访问共享资源的完整性(有你没我)

      POSIX 线程中同步:使用信号量实现

      信号量 : 表示一类资源,它的值表示资源的个数

    对资源访问:
      p操作(申请资源) [将资源的值 - 1]
      ....
      V操作(释放资源) [将资源的值 + 1]

    1.定义信号量
    sem_t sem ;

    2.初始化信号量
    int sem_init(sem_t *sem, int pshared, unsigned int value);
    参数:
      @sem 信号量
      @pshared 0:线程间使用
      @value 初始化的信号量的值
    返回值:
      成功返回0,失败返回-1

    3.P操作
      int sem_wait(sem_t *sem);

    4.V操作
      int sem_post(sem_t *sem);

    实例如下:
    创建两个线程
    write_thread : 从键盘上读取用户输入的数据,写到文件中
    read_thread : 从文件中读取数据,然后输出
    main_thread : 打开一个文件

    #include <head.h>
    
    sem_t read_sem;//读资源
    sem_t write_sem;//写资源
    
    
    void *write_thread(void *arg)
    {
        char buf[1024];
        int fd = *(int *)arg;
    
        while(1){
            //申请写资源
            if(sem_wait(&write_sem) < 0){
                perror("Fail to sem_wait");
                pthread_exit(NULL);
            }
    
            fgets(buf,sizeof(buf),stdin);
            buf[strlen(buf) - 1] = '';
    
            write(fd,buf,strlen(buf));
    
            //还原偏移量到上一次值
            lseek(fd,-strlen(buf),SEEK_CUR);
    
            //释放读资源
            if(sem_post(&read_sem) < 0){
                perror("Fail to sem_wait");
                pthread_exit(NULL);
            }
    
            if(strcmp(buf,"quit") == 0){
                break;
            }
    
        }
    
        pthread_exit(NULL);
    }
    
    void *read_thread(void *arg)
    {
        int n;
        char buf[1024];
        int fd = *(int *)arg;
    
        while(1){
            //申请读资源
            if(sem_wait(&read_sem) < 0){
                perror("Fail to sem_wait");
                pthread_exit(NULL);
            }
    
            n = read(fd,buf,sizeof(buf));
            buf[n] = '';
            printf("Read %d bytes : %s!
    ",n,buf);
    
            //释放写资源
            if(sem_post(&write_sem) < 0){
                perror("Fail to sem_wait");
                pthread_exit(NULL);
            }
    
            if(strcmp(buf,"quit") == 0){
                break;
            }
        }
    
        pthread_exit(NULL);
    }
    
    //./a.out  file
    int main(int argc, const char *argv[])
    {
        int fd;
        int ret;
        pthread_t rtid;
        pthread_t wtid;
    
        if(argc < 2){
            fprintf(stderr,"Usage : %s <filename>!
    ",argv[0]);
            exit(EXIT_FAILURE);
        }
    
        fd = open(argv[1],O_RDWR | O_CREAT | O_TRUNC,0666);
        if(fd < 0){
            fprintf(stderr,"Fail to open %s : %s
    ",argv[1],strerror(errno));
            exit(EXIT_FAILURE);
        }
    
        if(sem_init(&read_sem,0,0) < 0){
            perror("Fail to sem_init");
            exit(EXIT_FAILURE);
        }
    
        if(sem_init(&write_sem,0,1) < 0){
            perror("Fail to sem_init");
            exit(EXIT_FAILURE);
        }
    
        ret = pthread_create(&rtid,NULL,read_thread,&fd);
        if(ret != 0){
            fprintf(stderr,"Fail to pthread_create : %s!
    ",strerror(ret));
            exit(EXIT_FAILURE);
        }
    
        ret = pthread_create(&wtid,NULL,write_thread,&fd);
        if(ret != 0){
            fprintf(stderr,"Fail to pthread_create : %s!
    ",strerror(ret));
            exit(EXIT_FAILURE);
        }
        
        pthread_join(rtid,NULL);
        pthread_join(wtid,NULL);
    
        return 0;
    }
  • 相关阅读:
    关于在ubuntu下配置AMD显卡驱动的总结
    自然语言交流系统 phxnet团队 创新实训 个人博客 (十四)
    自然语言交流系统 phxnet团队 创新实训 个人博客 (十三)
    自然语言交流系统 phxnet团队 创新实训 个人博客 (十二)
    Qt编写地图综合应用4-仪表盘
    Qt编写地图综合应用3-省市区域图
    Qt编写地图综合应用2-迁徙图
    Qt编写地图综合应用1-闪烁点图
    个人总结的编程经验语录
    Qt编写的项目作品23-手机通讯录
  • 原文地址:https://www.cnblogs.com/bwbfight/p/9304626.html
Copyright © 2011-2022 走看看