C++学习:第六章Linux高级编程 - (十二)多线程概念、互斥锁、多线程同步、信号、条件量、信号量

一、 多线程

   1. 了解多线程

解决多任务实现。

历史上Unix服务器不支持多线程

Unix/Linux上实现多线程有两种方式:

1)内核支持多线程

2)使用进程的编程技巧封装进程实现多线程:轻量级多线程(现在的)

多线程的库:

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); //传递线程代码的数据

 

结论:

     1). 程序结束所有子线程就结束

解决办法:等待子线程结束

sleep/pause

int pthread_join(

pthread_t tid, //等待子线程结束

void **re); //子线程结束的返回值

 

     2). 创建子线程后,主线程继续完成系统分配时间片。

     3). 子线程结束就是线程函数返回。

     4). 子线程与主线程有同等优先级别.

 

作业:

写一个程序创建两个子线程

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

void call()
{
    pthread_exit("Kill");
    return ;
}

void* run(void* data)
{
    while(1)
    {
        printf("我是线程%s\n",data);
        sched_yield();

        //下面两个方法返回都可以
        //return "hello";
        pthread_exit("world");
    }
}

main()
{
    pthread_t  tid;
    char *re;
    pthread_create(&tid,0,run,"Jack");
    pthread_join(tid,(void**)&re);
    printf("%s\n",re);
}

 

     3. 线程的基本控制

线程的状态:

ready->runny->deady

|

sleep/pause

结束线程?

内部自动结束:(建议)

return  返回值(指针类型); (在线程函数中使用)

void pthread_exit(void*); (在任何线程代码中使用)

 

外部结束一个线程.

pthread_cancel(pthread_t);

线程开始

 

      4. 多线程的问题

本质上是数据脏的问题

 

      5. 多线程问题的解决

互斥锁(互斥量) mutex

锁的概念本质上也是共享内存

 

5.1 定义互斥量 pthread_mutex_t

5.2 初始化互斥量,默认是1 pthread_mutex_init

5.3 互斥量操作

置0 通过 phtread_mutex_lock 函数实现

判定互斥量:

如果是0,阻塞

如果是1,将值变为0,返回

置1 通过 pthread_mutex_unlock 函数实现

不管值是多少,直接置为1,返回

强烈要求成对使用(非必须,如果不解锁会变成死锁)

 

5.4 释放互斥量pthread_mutex_destroy

 

解释互斥量的使用

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

//1.
pthread_mutex_t m;
int a=0,b=0;
void display()
{
    //3
}

void *r1()
{
    while(1)
    {
        pthread_mutex_lock(&m);
        a++;
        b++;
        if(a!=b)
        {
            printf("%d!=%d\n",a,b);
            a=b=0;
        }

        pthread_mutex_unlock(&m);
    }
}

void *r2()
{
    while(1)
    {

        pthread_mutex_lock(&m);
        a++;
        b++;

        if(a!=b)
        {
            printf("%d!=%d\n",a,b);
            a=b=0;
        }
        pthread_mutex_unlock(&m);
    }
}

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);
}
#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();
}

结论:

互斥量保证锁定的代码一个线程执行,但不能保证必需执行完!

5.5 在lock与unlock之间调用pthread_exit,或者在线程外部调用pthread_cancel。

其他线程被永久死锁.

5.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_mutex_lock(&m);
        printf("%d\n",i);
        pthread_cleanup_pop(1);
    }
}

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注册的是进程的信号处理函数,与线程无关。但是确实可以向线程发送信号。

这个信号属于这个线程 也属于这个进程。

#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);
}

pthread_kill+sigwait控制进程

1.1定义信号集合

1.2.初始化信号集合

1.3.等待信号

1.4.其他线程发送信号

1.5.清空信号集合

案例:

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

pthread_cond_*** 与sigwait都是进程同步控制

pthread_cond_***稳定

pthread_cond_***在环境下不会死锁.

 

使用pause+pthread_kill

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

pthread_t t1,t2;
void handle(int s)
{
}

void *r1(void* d)
{
    while(1)
    {
        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+pthread_kill

如果是先发新后后等待接收,程序会因为异常信号终止。

#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);
        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);
}

 

使用pthread_cond

#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);
        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);
}

 

死锁代码

#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);
    //pthread_cond_wait(&c,&m2);//死锁
    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);
}

 

使用curses写一个多线程程序:

开启26个线程.每个线程控制一个字母在屏幕上掉落

建议每隔字母的高度随机.

#include <pthread.h>
#include <curses.h>
#include <math.h>

pthread_t t[26];
pthread_mutex_t m;

struct  AChar
{
    int x;
    int y;
    int speed;
    char a;
};

void *run(void *d)
{
    struct AChar a;
    a.x=rand()%COLS;
    a.y=rand()%(LINES/4);
    a.speed=1+rand()%3;
    a.a=65+rand()%26;

    while(1)
    {
        //改变对象的y坐标
        a.y+=a.speed;
        if(a.y>=LINES)
        {
            a.y=rand()%(LINES/4);
        }

        //绘制屏幕上
        pthread_mutex_lock(&m);
        mvaddch(a.y,a.x,a.a);

        //刷屏
        refresh();
        pthread_mutex_unlock(&m);

        //暂停
        usleep(10000);
    }
}


main()
{
    int i;
    initscr();
    pthread_mutex_init(&m,0);
    for(i=0;i<26;i++)
    {
        //随机产生字母与位置
        pthread_create(&t[i],0,run,0);
    }

    getch();
    pthread_mutex_destroy(&m);
    endwin();
}

 

改进版,针对刷屏部分

#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();
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值