LinuxC语言并发程序笔记补充

锁除了互斥锁,还有一个读写锁,进程通信还有对互斥锁优化的条件变量和死锁的概念。

一、死锁(Dead-lock)

概念:多个线程各自已握住至少一把锁,又试图去获取别人手里的锁,形成环形等待链;因为锁不可被外力剥夺,整个环路永久阻塞。

作用:本身无正面作用,是"锁使用不当"的产物;理解它是为了在设计上破坏四个必要条件(互斥、占有且等待、不可抢占、循环等待),让系统可持续推进。

函数api:

同互斥锁

最小示例:

cpp 复制代码
pthread_mutex_t m1=PTHREAD_MUTEX_INITIALIZER,
                m2=PTHREAD_MUTEX_INITIALIZER;
void *t1(void *){
    pthread_mutex_lock(&m1);
    sleep(1);
    pthread_mutex_lock(&m2);    // 等待 t2 的 m2
    pthread_mutex_unlock(&m2);
    pthread_mutex_unlock(&m1);
    return NULL;
}
void *t2(void *){
    pthread_mutex_lock(&m2);
    sleep(1);
    pthread_mutex_lock(&m1);    // 等待 t1 的 m1 → 循环等待
    pthread_mutex_unlock(&m1);
    pthread_mutex_unlock(&m2);
    return NULL;
}

pthread_mutex_t m1=PTHREAD_MUTEX_INITIALIZER是一种比较特殊的宏初始化用法。

因为m1和m2都需要两个锁,但同一时间他们都分别申请了1和2,而对于m1,它只有锁1,要锁2,而m2持有锁2,需要锁1。他们拿着锁也不放手,他们就都会卡死,这就是死锁。

二、读写锁(Reader-Writer Lock)

概念:把"锁"细分成"读锁"与"写锁"两种权限;读锁可被多个线程同时持有,写锁只能被一个线程持有,且写锁与任何锁互斥。是互斥锁pro,因为其支持一对多。

作用:在读操作远多于写操作的场景下,允许读者并行进入临界区,降低串行度,提升整体吞吐量,同时仍保证写操作的原子性与可见性。

函数api:

|------------------------------------------|-----------|
| pthread_rwlock_rdlock | 阻塞加读锁 |
| pthread_rwlock_wrlock | 阻塞加写锁 |
| pthread_rwlock_unlock | 解锁(读/写通用) |
| pthread_rwlock_tryrdlock / trywrlock | 非阻塞版本 |

用法和互斥锁的几乎一致

最小示例:

cpp 复制代码
pthread_rwlock_t rw=PTHREAD_RWLOCK_INITIALIZER;
int data=0;

void *reader(void *){
    pthread_rwlock_rdlock(&rw);
    printf("read %d\n", data);
    pthread_rwlock_unlock(&rw);
    return NULL;
}
void *writer(void *){
    pthread_rwlock_wrlock(&rw);
    data++;
    printf("write %d\n", data);
    pthread_rwlock_unlock(&rw);
    return NULL;
}

多个 reader 可同时持有读锁;但writer 必须独占写锁。

三、条件变量(Condition Variable)

概念:一种"等待-通知"原语,与互斥锁配合使用;线程可以在"条件尚未成立"时原子地释放锁并进入休眠,条件成立后被其他线程显式唤醒,再自动重新获得锁。

作用:把"反复轮询条件"改为"事件驱动",既避免 CPU 空转,又能在条件变化的瞬间精准唤醒等待线程,实现高效的同步与调度。

函数api:

|--------------------------|--------------------|
| pthread_cond_wait | 原子地:解锁→阻塞→被唤醒后重新加锁 |
| pthread_cond_signal | 唤醒一个等待线程 |
| pthread_cond_broadcast | 唤醒全部等待线程 |
| pthread_cond_timedwait | 最多等待指定时间 |

最小示例:

cpp 复制代码
pthread_mutex_t m=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  c=PTHREAD_COND_INITIALIZER;
int count=0;

void *prod(void *){
    pthread_mutex_lock(&m);
    count++;
    printf("produce %d\n", count);
    pthread_cond_signal(&c);   // 通知消费者
    pthread_mutex_unlock(&m);
    return NULL;
}
void *cons(void *){
    pthread_mutex_lock(&m);
    while(count==0)            // 防止虚假唤醒
        pthread_cond_wait(&c,&m);
    count--;
    printf("consume %d\n", count);
    pthread_mutex_unlock(&m);
    return NULL;
}

消费者先拿到锁却发现无数据时,通过 cond_wait 函数会释放锁并睡眠;生产者修改条件后 signal 将其唤醒,消费者在通过 cond_wait 函数拿回锁,进行读写操作。

相关推荐
老当益壮梁奶奶4 分钟前
Linux软件编程学习笔记(十一):深入浅出 TCP 协议与 Socket 编程实践
linux·c语言·笔记·学习·tcp/ip
清钟沁桐12 分钟前
mlir 编译器学习笔记之十四 -- cpu算子自动生成
笔记·学习·mlir
speop16 分钟前
hell-gpu| TASK01-2
linux·运维·算法
小蒜学长17 分钟前
借助于大模型工具Cursor的中药材交易系统的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端
minglie119 分钟前
多项式分裂域的自同构群的求法
学习
byte轻骑兵29 分钟前
【BlueZ 】用户态入口:main.c 核心函数与程序启动流程
linux·人工智能·bluez·电脑蓝牙·嵌入式蓝牙
每天题库39 分钟前
机修钳工题库计算题专练,尺寸公差公式熟练应用
学习·安全·考试·题库·考证
Seraphina3640 分钟前
记一次实验:利用缓存服务器规范化技术进行网页缓存欺骗
经验分享·笔记·网络安全·缓存
底层玩家老张1 小时前
数据库上K8s,运维为什么反而更累了?从StatefulSet到Operator的复盘
数据库·kubernetes·operator·数据库运维·架构选型
dear_bi_MyOnly1 小时前
数组字符串深度解析:从入门到卡牌实战
开发语言·c++·学习