线程同步的主要目的是确保多个线程在访问共享资源时能够按照一定的顺序进行,避免因为多个线程同时操作同一资源而导致的数据混乱或错误,以下解释 Linux 中线程同步的竞态条件和锁。
竞态条件和锁
当多个线程并发访问和修改同一个共享资源(如全局变量)时,如果没有适当的同步措施,就会遇到线程同步问题。这种情况下,程序最终的结果依赖于线程执行的具体时序,导致了竞态条件。
竞态条件(race condition)是一种特定的线程同步问题,指的是两个或者以上进程或者线程并发执行时,其最终的结果依赖于进程或者线程执行的精确时序。它会导致程序的行为和输出超出预期,因为共享资源的最终状态取决于线程执行的顺序和时机。为了确保程序执行结果的正确性和预期一致,需要通过适当的线程同步机制来避免竞态条件。
如何避免竞态条件
- 避免多线程写入一个地址。
- 给资源加锁,使同一时间操作特定资源的线程只有一个。
常见的锁机制
锁主要用于互斥,即在同一时间只允许一个执行单元(进程或线程)访问共享资源。包括上面的互斥锁在内,常见的锁机制共有三种:
(1)互斥锁(Mutex):保证同一时刻只有一个线程可以执行临界区的代码。
(2)读写锁(Reader/Writer Locks):允许多个读者同时读共享数据,但写者的访问是互斥的。
(3)自旋锁(Spinlocks):在获取锁之前,线程在循环中忙等待,适用于锁持有时间非常短的场景,一般是Linux内核使用。
后续
这里是学习中遇到的或想到的问题,以下都是个人总结。
2024-08-31
补充:关于读写锁中出现的写饥饿问题。
① 问题描述
读写锁的写饥饿问题(Writer Starvation)是指在使用读写锁时,写线程可能无限期地等待获取写锁,因为读线程持续地获取读锁而不断地推迟写线程的执行。这种情况通常在读操作远多于写操作时出现。
② 解决方案
Linux 提供了可以修改的属性 pthread_rwlockattr_t
,默认情况下,属性中指定的策略为“读优先”,当写操作阻塞时,读线程依然可以获得读锁,从而在读操作并发较高时导致写饥饿问题。我们可以尝试将策略更改为“写优先”,当写操作阻塞时,读线程无法获取锁,避免了写线程持有锁的时间持续延长,使得写线程获取锁的等待时间显著降低,从而避免写饥饿问题。
③ 个人总结
没有妥善管理读写操作的优先级,导致写者无限期等待资源。