黑狐家游戏

并发处理方式,并发处理可能带来哪三类问题及对策

欧气 3 0

《并发处理中的三类问题剖析与应对策略》

一、并发处理可能带来的三类问题

1、数据竞争问题

并发处理方式,并发处理可能带来哪三类问题及对策

图片来源于网络,如有侵权联系删除

表现形式

- 在并发环境下,多个线程或进程同时访问和修改共享数据时,就可能发生数据竞争,在一个多线程的银行账户管理系统中,如果没有适当的控制机制,多个线程同时对同一个账户进行取款操作,假设账户初始余额为1000元,线程A读取余额为1000元,准备取款500元;线程B也读取余额为1000元,也准备取款500元,如果没有正确的并发控制,两个线程可能都认为账户余额足够,从而分别执行取款操作,最终账户余额可能被错误地更新为0元,而不是正确的0元(1000 - 500 - 500)。

根源分析

- 现代计算机系统中,CPU为了提高执行效率,采用了缓存机制,每个CPU核心都有自己的缓存,当多个线程在不同的CPU核心上运行时,它们可能从各自的缓存中读取数据,而不是直接从主存中读取,这样就可能导致一个线程对共享数据的修改不能及时被其他线程看到,从而引发数据不一致的问题,指令的重排序也是导致数据竞争的一个因素,编译器和CPU为了优化程序的执行性能,可能会对指令的执行顺序进行调整,在并发环境下,这种调整可能会破坏程序的正确性。

2、死锁问题

表现形式

- 死锁是指多个进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去,有两个线程T1和T2,以及两个资源R1和R2,T1先获取了R1,然后试图获取R2;而T2先获取了R2,然后试图获取R1,由于它们都在等待对方释放自己所需要的资源,所以就陷入了死锁状态,在数据库系统中,也可能出现死锁情况,事务A锁住了表X中的一些行,并试图锁住表Y中的一些行;而事务B锁住了表Y中的一些行,并试图锁住表X中的一些行,这样两个事务就会互相等待,无法继续执行。

根源分析

- 死锁产生的主要原因有四个必要条件:互斥条件,即资源在某一时刻只能被一个进程或线程使用;请求和保持条件,进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放;不可剥夺条件,进程所获得的资源在未使用完之前,不能被其他进程强行夺走;循环等待条件,存在一组进程,它们互相等待对方所占有的资源,当这四个条件同时满足时,就会发生死锁。

3、活锁问题

表现形式

并发处理方式,并发处理可能带来哪三类问题及对策

图片来源于网络,如有侵权联系删除

- 活锁是指多个进程或线程在执行过程中,不断地改变自己的状态以响应其他进程或线程的变化,但始终无法推进自己的工作,在一个多线程的任务调度系统中,有两个线程T1和T2都在竞争执行某个任务,当T1准备执行任务时,发现T2也在竞争,于是T1放弃执行并等待一段时间;而T2发现T1放弃了,它也放弃执行并等待一段时间,然后T1又重新尝试,T2也重新尝试,如此反复,两个线程都无法真正开始执行任务,一直在互相“谦让”,导致任务无法完成。

根源分析

- 活锁通常是由于不恰当的并发控制策略导致的,在基于重试机制的并发控制中,如果重试的时机和策略没有设计好,就可能导致活锁,当多个进程或线程之间的交互过于频繁,并且缺乏有效的协调机制时,也容易产生活锁现象。

二、针对三类问题的对策

1、解决数据竞争问题的对策

锁机制

- 互斥锁(Mutex)是一种常用的解决数据竞争的方法,互斥锁保证在同一时刻只有一个线程能够访问被保护的共享资源,在上面提到的银行账户取款的例子中,可以为账户余额这个共享资源设置一个互斥锁,当一个线程要对账户余额进行操作时,它首先要获取这个互斥锁,如果锁已经被其他线程获取,那么这个线程就会被阻塞,直到锁被释放,这样就可以保证对账户余额的操作是原子性的,避免了数据竞争,除了互斥锁,还有读写锁(Read - Write Lock),读写锁允许多个线程同时对共享资源进行读操作,但在写操作时则是互斥的,这种锁适合于读操作频繁而写操作较少的场景,能够提高并发性能。

原子操作

- 现代编程语言和硬件都提供了原子操作的支持,原子操作是指不可被中断的一个或一系列操作,在C++ 11中,提供了原子类型(如std::atomic),可以用来定义原子变量,对于简单的共享数据类型,如整数,可以使用原子变量来确保对其的操作是原子性的,对一个原子整数进行自增或自减操作时,不会被其他线程打断,从而避免了数据竞争,在硬件层面,CPU提供了一些原子指令,如比较并交换(CAS - Compare - And - Swap)指令,通过使用这些原子指令,可以在不使用锁的情况下实现对共享数据的原子操作,这种无锁编程方式在高并发场景下能够提高性能,因为它避免了锁的获取和释放带来的开销。

2、解决死锁问题的对策

破坏死锁产生的必要条件

并发处理方式,并发处理可能带来哪三类问题及对策

图片来源于网络,如有侵权联系删除

- 针对互斥条件,在某些情况下可以通过资源的复制来避免互斥访问,在打印系统中,如果有多台打印机可供使用,而不是只有一台打印机作为共享资源,那么就可以避免多个进程对唯一一台打印机的互斥访问,对于请求和保持条件,可以采用一次性分配资源的策略,在操作系统中,进程在运行之前就一次性申请它所需要的所有资源,如果资源不足则等待,这样就避免了在运行过程中不断地请求新的资源而导致的死锁,对于不可剥夺条件,可以采用可剥夺资源的策略,在一些实时操作系统中,当高优先级的任务需要资源时,可以剥夺低优先级任务已经占有的资源,对于循环等待条件,可以采用资源有序分配策略,即要求进程按照一定的顺序申请资源,按照资源编号从小到大的顺序申请资源,这样就可以避免循环等待。

死锁检测与恢复

- 可以通过构建资源分配图等方法来检测死锁是否发生,在资源分配图中,节点表示进程和资源,边表示进程对资源的请求和分配关系,如果资源分配图中存在环,则可能存在死锁,一旦检测到死锁,可以采用多种恢复策略,终止一个或多个涉及死锁的进程,释放它们所占有的资源,从而打破死锁状态,或者通过抢占一些进程的资源,将这些资源分配给其他被阻塞的进程,以解除死锁。

3、解决活锁问题的对策

调整重试策略

- 在基于重试机制的并发控制中,需要合理设计重试的时间间隔和重试次数,可以采用指数退避的重试策略,即每次重试的时间间隔按照指数增长,这样可以避免多个线程或进程在短时间内不断地重试而导致活锁,在初始时,重试的时间间隔可以设置得较短,如10毫秒,随着重试次数的增加,时间间隔可以变为20毫秒、40毫秒、80毫秒等,还可以设置一个最大重试次数,当超过这个次数后,可以采取其他措施,如报错或者采用更激进的资源获取策略。

引入协调机制

- 可以使用信号量或事件等协调机制来避免活锁,在多线程的任务调度系统中,可以使用一个信号量来控制任务的执行,当一个线程想要执行任务时,它首先要获取这个信号量,如果信号量不可用,那么这个线程就会被阻塞,而不是像活锁情况那样不断地重试,当任务执行完成后,释放信号量,这样就可以确保任务能够按照一定的顺序被执行,避免了活锁现象,也可以采用集中式的协调器来管理多个进程或线程之间的交互,协调器可以根据系统的状态来决定哪个进程或线程可以执行,从而避免活锁。

标签: #并发处理 #问题分类 #对策 #并发问题

黑狐家游戏
  • 评论列表

留言评论