网络通信day05

线程间竞争

一、基本概念

    原子操作:中途不会被打断的操作称为原子操作(不会被其他线程竞争影响的操作)

    竞争与同步:

        同一个进程中的线程共享进程中绝大多数资源,当它们随意竞争时可能导致资源被破坏、脏数据、不完整、不一致的情况

        通过一些方法让线程在竞争资源时相互协调,避免出现以上情况,这种线程间协同工作称为线程同步

    临界区和临界资源:

        被多个线程同时访问的代码称为临界区、被同时访问的资源称为临界资源

二、互斥量(互斥锁)

    有些系统的man手册没有关于mutex的文档需要安装:

    sudo apt-get install manpages-posix-dev

    pthread_mutex_t 是一种数据类型 可以定义互斥变量

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

    功能:初始化一个互斥量

    mutex: 要初始化的互斥量变量

    attr: 对互斥量的属性进行设置,一般给NULL即可

    注意: 一般默认是开锁状态,也可使用 PTHREAD_MUTEX_INITIALIZER 对互斥量进行初始化

    例如:  

        pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

    int pthread_mutex_lock(pthread_mutex_t *mutex);

    功能:对互斥量进行加锁,成功则继续执行下文,失败则阻塞,直到互斥量被解锁并枷锁成功,才返回

    int pthread_mutex_trylock(pthread_mutex_t *mutex);

    功能:对互斥量尝试加锁,成功(0)或者失败(EBUSY)都立即返回

    int pthread_mutex_unlock(pthread_mutex_t *mutex);

    功能:对互斥量解锁

    int pthread_mutex_destroy(pthread_mutex_t *mutex);

    功能:销毁互斥量

三、信号量

    与XSI中的信号量原理相同,相当于线程之前使用的同一个计数器,用于统计、控制访问有限的共享资源的线程数量

    int sem_init(sem_t *sem, int pshared, unsigned int value);

    功能:初始化信号量

    sem: 被初始化的信号量

    pshared:

        0       只能在本进程内使用

        非0     表示该信号量可以以共享的内存的形式,让多个进程共享使用  (Linux不支持)

    value: 信号量的初始值

    int sem_wait(sem_t *sem);

    功能:对信号量-1,如果信号量为0不够减,则阻塞,减成功则继续执行

    int sem_trywait(sem_t *sem);

    功能:对信号量尝试-1,成功(0)或失败(EAGAIN)都立即返回

    int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

    功能: 对信号量减1,如果不够减则等待abs_timeout时间,如果超时返回ETIMEOUT错误编码    

    int sem_post(sem_t *sem);

    功能: 对信号量加1

    int sem_destroy(sem_t *sem);

    功能:销毁信号量

四、死锁

    1、什么是死锁

        多个进程或者线程之间互相等待对方手中的资源,而且在得到新的资源之前不会主动释放手中的资源

            这样如果形成了等待环路,称之为死锁现象

    2、产生死锁的四大必要条件

        资源互斥:

            资源只有两种状态,只有资源可用和不可用状态,不能同时使用,同一时间内只能被一个进程或线程使用

        占有且请求:

            对已经得到资源的进程或线程,对旧资源保持占有并继续请求新的资源

        资源不可剥夺:

            资源已经分配给进程或线程后,不能被其他进程或线程强制获取、除非资源的占有者主动释放

        环路等待:

            当死锁发生时,系统中必定有两个或两个以上的进程或线程执行路线形成一条等待的环路

    注意:以上四个条件同时成立就会形成死锁,并且死锁一旦产生基本无解,以现在的操作系统是无法解决死锁

        因此只能防止死锁的产生

    3、如何防止死锁的产生

        破坏资源互斥:

            想办法让资源能够共享使用

            缺点:受现实环境和资金的影响无法让资源共享

        破坏占用且请求:

            采用预分配的方式,让进程或线程在运行前一次性申请所有资源,如果在资源没有满足时不投入运行

            缺点:系统资源的占用会严重浪费,因为有些资源可能开始时使用,但是有些资源可能最后才使用

        破坏资源不可被剥夺:

            当一个进程或线程已经占用一个不可被剥夺的资源,并且请求资源无法被满足时

                则释放已经占用的资源,等待一段时间后重新申请    

            缺点:该策略实现比较麻烦,而且释放已经申请的资源可能导致前一阶段的工作无效

                反复地申请释放资源也会增加系统开销、占用CPU和寄存器、内存等资源

        破坏等待环路:

            给每个资源起编号,进程或线程按照编号以此请求资源,并且只有拿到前一个资源,才能继续请求下一个资源

            缺点:资源的编号相对稳定,当资源增加或删除时收到很大影响

    4、如何判断死锁

        1、画出资源分配图

        2、简化资源分配图

        3、使用死锁判断原理: 如果没有环路一定不会出现死锁

    了解:银行家算法

五、条件变量

    当某些条件满足时,可以让线程自己进入睡眠,也可以当某些条件满足时唤醒正在睡眠的线程

    int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);

    功能:初始化条件变量

    cond: 要初始化的条件变量

    attr: 默认给NULL即可

    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

    注意:也可以使用 PTHREAD_COND_INITIALIZER 赋值的方式初始化

    int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

    功能: 让当前线程睡入cond,并解锁mutex

    返回值:直到线程被唤醒才返回

    int pthread_cond_signal(pthread_cond_t *cond);

    功能: 唤醒cond中正在睡眠的一个线程,在唤醒前要确保锁

    打开状态,当线程醒来时,该线程会自动把锁重新加上

    int pthread_cond_broadcast(pthread_cond_t *cond);

    功能: 唤醒cond中所有线程,线程是否醒来取决于能否再次加锁

    int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,

        const struct timespec *restrict abstime);

    功能: 让当前线程睡入cond,并解锁mutex,只睡眠abstime时间,超时会被操作系统唤醒

    int pthread_cond_destroy(pthread_cond_t *cond);

    功能:销毁条件变量

    注意:使用条件变量可以实现生产者与消费者模型

六、生产者与消费者模型

    生产者:生产数据的线程

    消费者:使用数据的线程

    仓库:临时存放数据的缓冲区(仓库解决了生产、消费不匹配)  

    可能产生的问题:

        消费快于生产:仓库空虚、饿死

        生产快于消费: 仓库爆满、撑死

   

    使用条件变量来解决以上问题:

        当缓冲区空的时候,消费者线程睡入条件变量(empty),通知生产者线程全部醒来(full)

        当缓冲区满的时候,生产者线程睡入条件变量(full),通知消费者线程全部醒来(empty)

七、哲学家进餐问题

    有五个哲学家,他们的生活方式是交替地进行思考和进餐,哲学家们共用一张圆桌,分别坐在周围地五张椅子上

        在圆桌上有五个碗和五只筷子,平时哲学家进行思考,饥饿时便试图取其左、右最靠近他的筷子,

        只有在他拿到两支筷子时才能进餐,该哲学家进餐完毕后,放下左右两只筷子又继续思考。

    约束条件

    (1)只有拿到两只筷子时,哲学家才能吃饭。

    (2)如果筷子已被别人拿走,则必须等别人吃完之后才能拿到筷子。

    (3)任一哲学家在自己未拿到两只筷子吃完饭前,不会放下手中已经拿到的筷子。

一些进程和线程相关的问题:

    1、进程与线程的区别

    2、进程处理多任务时需要解决什么问题(进程间通信的问题)

    3、线程处理多任务时需要解决什么问题

    4、TCP服务器的编程模型有哪几种? 以及它们的优缺点?

    5、随着客户端的连接和退出越来越频繁,服务端都需要频繁地创建、销毁线程,该过程会比较耗时,如何解决?

Windows编程准备工作:

    1、解压编译器mingw到c盘

    2、复制:   C:\mingw\bin 路径

    3、右击此电脑->属性->高级系统设置->高级->环境变量->Path->编辑->新建->粘贴路径->确定

    4、win+r->输入cmd->输入gcc -v 测试编译器是否添加成功(部分电脑需要要管理员权限打开cmd再测试)

    5、下载 VSCode 安装三个插件

    VSCode配置:

    1、设置->扩展->Run Code...->Run in Termial 等三项打钩

    2、Exectuor Map->编辑->修改 ->保存

    "c": "cd $dir && gcc $fileName -std=gnu99 -lws2_32 -lpthread -o  $fileNameWithoutExt && $dir$fileNameWithoutExt",

    "cpp": "cd $dir && g++ $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",

Windows 与 Linux的socket网络编程区别

    1、额外加库 -lws2_32

    2、头文件不同 <windsock2.h> 其他都不需要

    3、先初始化网络库,固定的,背下来就好

    4、使用closesocket关闭socket描述符

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值