c++ 学习笔记(高级linux编程) day12

本文详细介绍Linux下多线程编程的基础知识与实践案例,包括多线程的创建、基本控制、常见问题及解决方案等内容。通过具体代码示例,帮助读者深入理解线程间的同步、互斥锁使用、条件变量等核心概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);//传递线程代码的数据
看一个小例子:

#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);//子线程结束的返回值

#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.子线程与主线程有同等优先级别.

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

#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*);(在任何线程代码中使用)  

#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为随机数和当前时间的显示

#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.多线程的问题
    数据脏

#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,返回
          置1pthread_mutex_unlock
              置1返回
       强烈要求成对使用       
    4.释放互斥量pthread_mutex_destroy

#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
     注意:
      这不是函数,而是宏.
       必须成对使用

#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.清空信号集合

#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
    ,则进程会接收信号进行默认处理

#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

#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来处理:

#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先执行。

#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_***在环境下不会死锁.
课堂练习:
   使用条件量与互斥构造死锁程序.

#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个线程.每个线程控制一个字母在屏幕上掉落
       建议每隔字母的高度随机.

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值