• linux高级编程day12 笔记


    一.多线程
      1.了解多线程
        解决多任务实现。
        历史上Unix服务器不支持多线程
        Unix/Linux上实现多线程有两种方式:
         内核支持多线程
         使用进程的编程技巧封装进程实现多线程:轻量级多线程
        多线程的库:
          libpthread.so   -lpthread

          pthread.h 

      2.创建多线程
         2.1.代码?
            回调函数
         2.2.线程ID?
            pthread_t
         2.3.运行线程?
            pthread_create
       int pthread_create(
         pthread_t *th,//返回进程ID
         const pthread_attr_t  *attr,//线程属性,为NULL/0,使用进程的默认属性
         void*(*run)(void*),//线程代码
         void *data);//传递线程代码的数据
    看一个小例子:

    View Code
    #include <stdio.h>
    #include <pthread.h>
    
    void *run(void *data)
    {
        printf("我是线程!\n");
    }
    main()
    {
        pthread_t tid;
        pthread_create(&tid, 0, run, 0);
    }

    运行程序,发现并没有输出。为什么呢?请看结论:
       结论:
         1.程序结束所有子线程就结束
           解决办法:等待子线程结束
                sleep/pause
           int pthread_join(
             pthread_t tid,//等待子线程结束
             void **re);//子线程结束的返回值

    View Code
    #include <stdio.h>
    #include <pthread.h>
    
    void *run(void *data)
    {
        printf("我是线程!\n");
    }
    main()
    {
        pthread_t tid;
        pthread_create(&tid, 0, run, 0);
        //sleep(1);
        pthread_join(tid, (void **)0);
    }

       
         2.创建子线程后,主线程继续完成系统分配时间片。
         3.子线程结束就是线程函数返回。
         4.子线程与主线程有同等优先级别.

    作业:
      写一个程序创建两个子线程

    View Code
    #include <stdio.h>
    #include <pthread.h>
    
    void *run(void *data)
    {
        printf("我是线程!\n");
    }
    void *run2(void *data)
    {
        printf("我是线程2!\n");
    }
    main()
    {
        pthread_t tid;
        pthread_t tid2;
        pthread_create(&tid, 0, run, 0);
        pthread_create(&tid2, 0, run2, 0);
        //sleep(1);
        pthread_join(tid, (void **)0);
        pthread_join(tid2, (void**)0);
    }                    

      3.线程的基本控制
        线程的状态:
          ready->runny->deady
               |
             sleep/pause
        结束线程?    
          内部自动结束:(建议)
           return  返回值;(在线程函数中使用)
           void pthread_exit(void*);(在任何线程代码中使用)  

    View Code
    #include <stdio.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <sched.h>
    void call()  //用一个函数来退出线程的时候,就可以看到return和exit的区别
    {
        pthread_exit("Kill");  //可以让线程结束
        return ; //只能让函数退出,并不能让线程退出
    }
    void* run(void* data)
    {
        while(1)
        {
            printf("我是线程!%s\n");
            sched_yield();  //放弃当前时间片。等待下一次执行
            //return "hello";
            pthread_exit("world");        
        }
    }
    main()
    {
        pthread_t  tid;
        char *re;
        pthread_create(&tid,0,run,0);    
        pthread_join(tid,(void**)&re);  //re接收返回值
        printf("%s\n",re);
        
    }                

          外部结束一个线程.      
           pthread_cancel(pthread_t);
    小应用: 用多线程来写7为随机数和当前时间的显示

    View Code
    #include <curses.h>
    #include <pthread.h>
    #include <time.h>
    #include <math.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    //全局变量两个窗体
    WINDOW *wtime,*wnumb;
    pthread_t thnumb,thtime;
    pthread_mutex_t m;
    //线程1:随机数
    void*runnumb(void *d)
    {
        int num;
        while(1)
        {
            //循环产生7位随机数
            num=rand()%10000000;
            pthread_mutex_lock(&m);
            //显示
            mvwprintw(wnumb,1,2,"%07d",num);
            //刷新
            refresh();
            wrefresh(wnumb);
            pthread_mutex_unlock(&m);
            usleep(1);
        }
        return 0;
    }
    //线程2:时间
    void*runtime(void*d)
    {    
        time_t tt;
        struct tm *t;
        while(1)
        {
            //循环取时间
            tt=time(0);
            t=localtime(&tt);
            pthread_mutex_lock(&m);
            //显示
            mvwprintw(wtime,1,1,"%02d:%02d:%02d",
                t->tm_hour,t->tm_min,t->tm_sec);
            //刷新
            refresh();
            wrefresh(wtime);
            pthread_mutex_unlock(&m);
            usleep(1);
        }
    }    
    
    main()
    {
        //初始化curses
        initscr();
        curs_set(0);
        noecho();
        keypad(stdscr,TRUE);
        wnumb=derwin(stdscr,3,11,
                (LINES-3)/2,(COLS-11)/2);
        wtime=derwin(stdscr,3,10,0,COLS-10);
        box(wnumb,0,0);
        box(wtime,0,0);
        refresh();
        wrefresh(wnumb);
        wrefresh(wtime);
        pthread_mutex_init(&m,0);//2
        //创建线程1
        pthread_create(&thnumb,0,runnumb,0);
        //创建线程2
        pthread_create(&thtime,0,runtime,0);
        //等待按键
        //结束
        getch();
        pthread_mutex_destroy(&m);//3
        delwin(wnumb);
        delwin(wtime);
        endwin();
    }

      4.多线程的问题
        数据脏

    View Code
    #include <stdio.h>
    #include <pthread.h>
    
    int a=0,b=0;
    void display()
    {
        a++;
        b++;        
        if(a!=b)
        {
            printf("%d!=%d\n",a,b);
            a=b=0;
        }
    }
    void *r1()
    {
        while(1)
        {
            display();
        }
    }
    
    void *r2()
    {
        while(1)
        {
            display();
        }
    }
    main()
    {
        pthread_t t1,t2;
        
        pthread_create(&t1,0,r1,0);
        pthread_create(&t2,0,r2,0);
        pthread_join(t1,(void**)0);
        pthread_join(t2,(void**)0);
    }

    看上去好像程序没有问题,但是会输出很多很多不相等的a,b数据,甚至很多a,b相等的也被输出来了。这就是两个线程之前共用数据时的问题。
      
      5.多线程问题的解决
        互斥锁/互斥量  mutex
        1.定义互斥量pthread_mutex_t
        2.初始化互斥量 默认是1 pthread_mutex_init
        3.互斥量操作  置0 phtread_mutex_lock
                判定互斥量0:阻塞
                     1:置0,返回
              置1 pthread_mutex_unlock
                  置1返回
           强烈要求成对使用       
        4.释放互斥量pthread_mutex_destroy

    View Code
    #include <stdio.h>
    #include <pthread.h>
    //1.定义互斥量
    pthread_mutex_t m;
    int a=0,b=0;
    void display()
    {
        //3.操作互斥量
        pthread_mutex_lock(&m);    
        a++;
        b++;        
        if(a!=b)
        {
            printf("%d!=%d\n",a,b);
            a=b=0;
        }
        pthread_mutex_unlock(&m);    
    }
    void *r1()
    {
        while(1)
        {
            display();
        }
    }
    
    void *r2()
    {
        while(1)
        {
            display();
        }
    }
    main()
    {
        pthread_t t1,t2;
        //2. 初始化互斥量
        pthread_mutex_init(&m,0);
        pthread_create(&t1,0,r1,0);
        pthread_create(&t2,0,r2,0);
        pthread_join(t1,(void**)0);
        pthread_join(t2,(void**)0);
        //4. 释放互斥量
        pthread_mutex_destroy(&m);
    }

       结论:
         互斥量保证锁定的代码一个线程执行,
         但不能保证必需执行完!           
        
        5.在lock与unlock之间,调用pthread_exit?
          或者在线程外部调用pthread_cancel?
         其他线程被永久死锁.
        6.pthread_cleanup_push {
         pthread_cleanup_pop  } 
         这对函数作用类似于atexit
         注意:
          这不是函数,而是宏.
           必须成对使用

    View Code
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    pthread_mutex_t m;
    void handle(void *d)
    {
        printf("退出后的调用!\n");
        pthread_mutex_unlock(&m);
    }
    
    void* runodd(void *d)
    {
        int i=0;    
        for(i=1;;i+=2)
        {    
                
            pthread_cleanup_push(handle,0);  //pthread_exit()和pthread_cancle()以及pthread_cleanup_pop(1)参数为1时会触发
            pthread_mutex_lock(&m);
            printf("%d\n",i);        
            pthread_cleanup_pop(1);        //参数为1就会弹出handle并执行函数,如果参数为0,则只弹出,不执行函数。        
        }
    }
    
    void* runeven(void *d)
    {
        int i=0;
        for(i=0;;i+=2)
        {
            pthread_cleanup_push(handle,0);
            pthread_mutex_lock(&m);        
            printf("%d\n",i);        
            pthread_cleanup_pop(1);
            
        }
    }
    
    
    main()
    {
        pthread_t todd,teven;
        pthread_mutex_init(&m,0);
        pthread_create(&todd,0,runodd,0);
        pthread_create(&teven,0,runeven,0);
        sleep(5);
        pthread_cancel(todd);
        pthread_join(todd,(void**)0);
        pthread_join(teven,(void**)0);
        pthread_mutex_destroy(&m);
    }    

      6.多线程的应用

    二.多线程同步
      互斥量/信号/条件量/信号量/读写锁
       1.sleep与信号
        pthread_kill向指定线程发送信号
        signal注册的是进程的信号处理函数.
        
        pthread_kill+sigwait控制进程
        1.1.定义信号集合
        1.2.初始化信号集合
        1.3.等待信号 
        1.4.其他线程发送信号  
        1.5.清空信号集合

    View Code
    #include <stdio.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <signal.h>
    pthread_t t1,t2;
    sigset_t sigs;
    void handle(int s)
    {
        printf("信号!\n");
    }
    void*r1(void*d)
    {    
        int s;
        while(1)
        {
            printf("线程--1\n");
            sigwait(&sigs,&s);
            printf("接收到信号:%d!\n",s);
        }
    }
    void*r2(void*d)
    {
        
        while(1)
        {
            printf("线程----2\n");
            sleep(2);
            pthread_kill(t1,SIGUSR1);
        }
    }
    
    main()
    {
        sigemptyset(&sigs);
        //sigaddset(&sigs,SIGUSR1);
        sigfillset(&sigs);
        //signal(SIGUSR1,handle);
        pthread_create(&t1,0,r1,0);
        pthread_create(&t2,0,r2,0);
        
        pthread_join(t1,(void**)0);
        pthread_join(t2,(void**)0);
        
        
    }

    案例:
        sigwait实际处理了信号
        如果进程没有处理信号,目标线程也没有sigwait
        ,则进程会接收信号进行默认处理

    View Code
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <sched.h>
    sigset_t sigs;
    pthread_t todd,teven;
    void* runodd(void *d)
    {
        int i=0;    
        int s;
        for(i=0;;i+=2)
        {        
            printf("%d\n",i);                
            sigwait(&sigs,&s);
                    
        }
    }
    
    void* runeven(void *d)
    {
        int i=0;
        int s;
        for(i=1;;i+=2)
        {
            printf("%d\n",i);
            sleep(1);                
            pthread_kill(todd,34);        
        }
    }
    
    
    main()
    {    
        sigemptyset(&sigs);
        sigaddset(&sigs,34);    
        pthread_create(&todd,0,runodd,0);
        pthread_create(&teven,0,runeven,0);
        pthread_join(todd,(void**)0);
        pthread_join(teven,(void**)0);
        
    }

       2.条件量
          信号量类似
          2.1.定义条件量
          2.2.初始化条件量
          2.3.等待条件量 
          2.4.其他线程修改条件量
          2.5.释放条件量

    案例:
       创建两个线程.
        一个线程等待信号
        一个线程每隔1秒发送信号
        1.使用pause+pthread_kill

    View Code
    #include <stdio.h>
    #include <pthread.h>
    #include <signal.h>
    pthread_t t1,t2;
    void handle(int s)  //什么也不干。但是可以防止t1没有sigwait()处理信号,程序异常退出。
    {
    }
    void *r1(void* d)
    {
        while(1)
        {
            pause();     //pause受信号影响,不起作用了。所以程序一直循环打印。
            printf("活动!\n");
        }
    }
    void *r2(void* d)
    {
        while(1)
        {
            sleep(1);
            pthread_kill(t1,34);
        }
    }
    main()
    {
        signal(34,handle);
        pthread_create(&t1,0,r1,0);
        pthread_create(&t2,0,r2,0);
        pthread_join(t1,(void**)0);
        pthread_join(t2,(void**)0);
    }

    上面这种方式看起来不太好,有一个怪怪的函数,什么也没干。下面用sigwait来处理:

    View Code
    #include <stdio.h>
    #include <pthread.h>
    #include <signal.h>
    pthread_t t1,t2;
    sigset_t sigs;
    void *r1(void* d)
    {
        int s;
        while(1)
        {        
            sigwait(&sigs,&s);    
            printf("活动!\n");
        }
    }
    void *r2(void* d)
    {
        while(1)
        {        
            sleep(1);  //sigwait必须先于pthread_kill执行,
                                 //才能正确接收到信号,不然也会异常退出。
                                 //所以睡眠一会儿,保证每次kill之前都已经wait了。
            pthread_kill(t1,34);
        }
    }
    main()
    {
        sigemptyset(&sigs);
        sigaddset(&sigs,34);
        pthread_create(&t1,0,r1,0);
        pthread_create(&t2,0,r2,0);
        pthread_join(t1,(void**)0);
        pthread_join(t2,(void**)0);
    }

    上面这种方法的缺陷如sleep()后面的注释。
    看下面条件量的处理:
    条件量则没有上面的那个问题,不用等wait先执行。

    View Code
    #include <stdio.h>
    #include <pthread.h>
    #include <signal.h>
    pthread_t t1,t2;
    pthread_cond_t cond;//1.
    pthread_mutex_t m;
    void *r1(void* d)
    {
        int s;
        while(1)
        {            
            pthread_cond_wait(&cond,&m);    //在非互斥的线程里面,m参数形同虚设。
                                                                        //如果在互斥的线程里,m可以解锁,让另一个线程执行,
                                                                        //并发出信号让wait接收,防止死锁。
            printf("活动!\n");
        }
    }
    void *r2(void* d)
    {
        while(1)
        {            
            pthread_cond_signal(&cond);  //条件量不累计,发多个也相当于一个的效果。
            pthread_cond_signal(&cond);
            pthread_cond_signal(&cond);
            sleep(10);
        }
    }
    main()
    {
        pthread_mutex_init(&m,0);
        pthread_cond_init(&cond,0);//2
        pthread_create(&t1,0,r1,0);
        pthread_create(&t2,0,r2,0);
        pthread_join(t1,(void**)0);
        pthread_join(t2,(void**)0);
        pthread_cond_destroy(&cond);
        pthread_mutex_destroy(&m);
    }

       pthread_cond_*** 与sigwait都是进程同步控制
       
       pthread_cond_***稳定
       pthread_cond_***在环境下不会死锁.
    课堂练习:
       使用条件量与互斥构造死锁程序.

    View Code
    #include <stdio.h>
    #include <pthread.h>
    
    pthread_t t1,t2;
    pthread_mutex_t m1,m2;
    pthread_cond_t c;
    
    void* r1(void*d)
    {
        while(1)
        {
            pthread_mutex_lock(&m1);  
            printf("我是等待!\n");
            pthread_cond_wait(&c,&m1); //如果这里是m2,则不能解r2的锁,造成死锁
            pthread_mutex_unlock(&m1);
        }
    }
    
    void* r2(void *d)
    {
        while(1)
        {
            pthread_mutex_lock(&m1);
            printf("我是让你不等待!\n");
            pthread_cond_signal(&c);        
            pthread_mutex_unlock(&m1);
        }
    }
    main()
    {
        pthread_cond_init(&c,0);
        pthread_mutex_init(&m1,0);
        pthread_mutex_init(&m2,0);
        
        pthread_create(&t1,0,r1,0);
        pthread_create(&t2,0,r2,0);
        
        pthread_join(t1,0);
        pthread_join(t2,0);
        
        pthread_mutex_destroy(&m2);
        pthread_mutex_destroy(&m1);
        pthread_cond_destroy(&c);
        
    }

    作业:
       1.写一个程序:
         两个线程写数据到文件.
           数据格式:日期时间,线程ID\n
       要求:
         要求使用互斥, 保证数据正确.
         体会使用互斥和不使用互斥的异同.
         
       2.使用curses写一个多线程程序
         开启26个线程.每个线程控制一个字母在屏幕上掉落
           建议每隔字母的高度随机.

    View Code
    #include <pthread.h>
    #include <curses.h>
    #include <math.h>
    struct  AChar
    {
        int x;
        int y;
        int speed;
        char a;
    };
    int stop=1;
    pthread_t t[26];
    pthread_t tid;
    pthread_mutex_t m;
    struct AChar  a[26];
    
    void *run(void *d)
    {    
        int id;
        static idx=-1;
        idx++;
        id=idx;
        while(stop)
        {
            pthread_mutex_lock(&m);
            //改变对象的y坐标
            a[id].y+=a[id].speed;
            if(a[id].y>=LINES)
            {
                a[id].y=rand()%(LINES/4);
            }
            pthread_mutex_unlock(&m);
            sched_yield();        
            usleep(100000);
        }
    }
    void * update(void *d)
    {
        int i=0;
        while(stop)
        {
            erase();
            //绘制屏幕上
            for(i=0;i<26;i++)
            {    
                mvaddch(a[i].y,a[i].x,a[i].a);
            }
            //刷屏
            refresh();
            usleep(10000);
        }
        
    }
    
    main()
    {
        int i;
        initscr();
        curs_set(0);
        noecho();
        keypad(stdscr,TRUE);
        for(i=0;i<26;i++)
        {
            a[i].x=rand()%COLS;
            a[i].y=rand()%(LINES/4);
            a[i].speed=1+rand()%3;
            a[i].a=65+rand()%26;
        }    
        pthread_mutex_init(&m,0);    
        pthread_create(&tid,0,update,0);
        for(i=0;i<26;i++)
        {
            //随机产生字母与位置        
            pthread_create(&t[i],0,run,0);
        }    
        getch();
        stop=0;
        for(i=0;i<26;i++)
        {
            //随机产生字母与位置        
            pthread_join(t[i],(void**)0);
        }
        pthread_join(tid,(void**)0);
        pthread_mutex_destroy(&m);
        endwin();    
    }

          
       3.写一个程序:创建两个线程
          一个线程负责找素数.
          另外一个线程把素数保存到文件
        要求:
          找到以后,通知另外一个线程保存,停止招素数
          线程保存好以后通知素数查找线程继续查找.
        目的:
          互斥与信号/条件量作用是不同.

  • 相关阅读:
    Pandas DataFrame操作
    Pandas Series数据结构基本操作
    Locally weighted regression algorithm
    Python Numpy线性代数操作
    Python Numpy 矩阵级基本操作(2)
    分布式锁1 Java常用技术方案(转)
    设计模式
    设计模式
    设计模式
    Lock和synchronized比较详解(转)
  • 原文地址:https://www.cnblogs.com/tangzhengyue/p/2618469.html
Copyright © 2020-2023  润新知