线程 锁(二)

本文详细介绍了线程锁中的读写锁机制及其在多线程编程中的应用,并探讨了条件变量如何与互斥量结合使用来实现线程间的同步。





线程 锁(二)

线程 锁(二)

线程


读写锁

  1. 当读写锁是加写锁时, 其他试图加写锁的线程都会被阻塞, 加读锁的也会被阻塞到写锁释放为止.
  2. 读写锁是加写锁时, 其他试图加写锁的线程可以共享数据.

读写锁的函数

  
  1. #include <pthread.h>
  2. // 分配一个读写锁
  3. int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict *attr)
  4. // 释放读写锁
  5. int pthread_rwlock_destroy(pthread_rwlock_t rwlock)
  6. // 成功, 返回 0; 失败. 返回错误编码
  7. // 静态分配:
  8. pthread_rwlock_t t = PTHREAD_RWLOCK_INITALLZER;

读写锁在使用之前一定要初始化, 在释放他们底层的内存之前必须销毁
同样, 如果在调用pthread_rwlock_destroy之前就释放读写锁的空间, 那么分配的空间就会丢失

  
  1. #include <pthread.h>
  2. int pthread_rwlock_rlock(pthread_rwlock_t *rwlock) // 读锁
  3. int pthread_rwlock_wlock(pthread_rwlock_t *rwlock) // 写锁
  4. int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) // 解锁
  5. // 成功, 返回 0; 失败, 返回错误编码

条件变量

1. 条件变量给多个线程提供一个汇合的机会.
2. 条件变量与互斥量一起使用时, 允许线程无竞争的方式等待特定的条件变量执行.
3. 条件本身由互斥量保护的. 线程在改变条件之前应该首先锁住互斥量

条件变量创建

  
  1. #include <pthread.h>
  2. int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr)
  3. int pthread_cond_destroy(pthread_cond_t *restrict cond)
  4. // 成功, 返回 0; 失败, 返回错误编码

变量锁

  
  1. #include <pthread.h>
  2. int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex)
  3. // tsptr : 等待时间
  4. int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict tsptr)
  5. // 成功, 返回 0; 失败, 返回错误编码

唤醒等待条件的线程
这是再给线程或条件发信号

  
  1. #include <pthread.h>
  2. // 至少能唤醒一个等待条件的线程
  3. int pthread_cond_signal(pthread_cond_t *cond)
  4. // 唤醒所有等待条件的线程
  5. int pthread_cond_broadcast(pthread_cond_t *cond)
  6. // 成功, 返回 0; 失败, 返回错误编码

自己照着一篇博主写的代码, 发现写的并不是很好
这里是博主的地址, 写的很详尽的, 我也就不再多写了, 毕竟写的也不容易, 太花时间了, 还要准备上课, 不过写的希望对大家有帮助
地址 : https://www.cnblogs.com/yuuyuu/p/5140875.html


<think> 我们被要求回答关于Python线程的使用方法、原理及示例。根据提供的引用内容,我们可以总结如下: 引用[1]和[2]介绍了线程的基本概念和重要性:在多线程编程中,用于保证数据的一致性和线程安全,防止多个线程同时访问共享资源导致的数据竞争问题。 引用[3]提到了的类型,包括Lock、RLock、Semaphore、Event和Condition,并指出的使用场景和最佳实践。 引用[4]给出了一个使用的具体代码示例,展示了如何通过来保护一个共享状态(flag)的读写。 因此,我们的回答应该包括: 1. 线程的基本原理:为什么需要? 2. Python中常用的类型(Lock, RLock, Semaphore, Event, Condition)及其适用场景。 3. 的基本使用方法(尤其是Lock和RLock的示例)。 4. 使用的注意事项(如死问题)。 我们将按照以下结构组织回答: ### 1. 线程的基本原理 在多线程环境中,当多个线程同时访问和修改同一个共享资源时,如果没有适当的同步机制,可能会导致数据不一致。提供了一种互斥机制,确保在同一时刻只有一个线程可以访问共享资源。 ### 2. Python中的线程类型 - **Lock(互斥)**:最基本的,只能被获取一次,其他线程必须等待被释放。 - **RLock(可重入)**:同一个线程可以多次获取同一个,但必须释放相同次数。 - **Semaphore(信号量)**:控制同时访问资源的线程数量。 - **Event(事件)**:用于线程间的通信,一个线程发出事件信号,其他线程等待该事件。 - **Condition(条件变量)**:用于更复杂的线程同步,可以等待某个条件成立。 ### 3. 的使用方法 #### 3.1 使用`threading.Lock()` 基本步骤: 1. 创建:`lock = threading.Lock()` 2. 在访问共享资源前获取:`lock.acquire()` 3. 在访问后释放:`lock.release()` 4. 推荐使用`with`语句来自动管理的获取和释放。 示例代码: ```python import threading counter = 0 lock = threading.Lock() def increment(): global counter for _ in range(100000): with lock: # 自动获取和释放 counter += 1 threads = [threading.Thread(target=increment) for _ in range(5)] for t in threads: t.start() for t in threads: t.join() print(counter) # 输出500000 ``` #### 3.2 使用`threading.RLock()` RLock允许同一个线程多次获取,不会产生死。 示例代码: ```python import threading rlock = threading.RLock() def recursive_func(count): with rlock: if count > 0: print(f"Count: {count}") recursive_func(count-1) recursive_func(5) ``` ### 4. 死问题及其避免 死是指两个或多个线程互相等待对方释放,导致程序无法继续执行。避免死的方法包括: - 按固定顺序获取 - 使用超时机制(如`lock.acquire(timeout=5)`) - 使用上下文管理器(`with`语句)确保的释放 ### 5. 的性能考量 的获取和释放会带来一定的性能开销,因此应尽量减少的持有时间,避免在保护的代码块中执行耗时操作。 ### 6. 结论 合理使用线程是保证多线程程序正确性的关键。在Python中,推荐使用`with`语句来管理,以避免忘记释放导致的死问题。 §§相关问题§§ 1. Python中如何避免死? 2. 信号量(Semaphore)和互斥(Lock)有什么区别? 3. 在生产者-消费者模型中如何使用线程? </think> ### Python线程:使用方法、原理与示例 线程是Python多线程编程的核心同步机制,用于解决**数据竞争**和**线程安全问题**。当多个线程同时访问共享资源时,线程通过互斥访问确保数据一致性[^1][^3]。 --- ### 一、线程的核心原理 1. **互斥访问机制** 线程基于`acquire()`和`release()`操作实现: - 线程访问共享资源前必须调用`acquire()`获取 - 若已被占用,线程进入阻塞状态(等待队列) - 释放后,等待队列中第一个线程被唤醒 - 基本流程: $$ \text{acquire()} \rightarrow \text{访问资源} \rightarrow \text{release()} $$ 2. **内存屏障作用** 操作会强制刷新CPU缓存,确保线程获取最新共享数据(避免缓存不一致问题)[^3]。 --- ### 、Python线程类型及使用 Python的`threading`模块提供5种机制: #### 1. **互斥 (Lock)** - **原理**:基础,同一时刻仅允许一个线程访问资源 - **使用场景**:简单共享变量保护 - **示例**: ```python import threading counter = 0 lock = threading.Lock() # 创建 def increment(): global counter for _ in range(1000): lock.acquire() # 获取 counter += 1 lock.release() # 释放 threads = [threading.Thread(target=increment) for _ in range(10)] for t in threads: t.start() for t in threads: t.join() print(counter) # 输出10000 (无数据竞争) ``` #### 2. **可重入 (RLock)** - **原理**:允许同一线程多次获取(内部维护计数器) - **使用场景**:递归函数或嵌套调用场景 - **示例**: ```python rlock = threading.RLock() def recursive_func(n): with rlock: # 使用with自动管理 if n > 0: recursive_func(n-1) recursive_func(5) # 不会死 ``` #### 3. **信号量 (Semaphore)** - **原理**:控制同时访问资源的线程数量 - **使用场景**:连接池/资源池管理 - **示例**: ```python sem = threading.Semaphore(3) # 允许3个线程并发 def access_resource(): with sem: print(f"资源被访问,当前信号量: {sem._value}") time.sleep(1) ``` #### 4. **事件 (Event)** - **原理**:线程间通信机制,通过标志位同步 - **使用场景**:生产者-消费者模型 - **示例**: ```python event = threading.Event() def consumer(): event.wait() # 阻塞直到事件触发 print("收到事件通知") def producer(): time.sleep(2) event.set() # 触发事件 ``` #### 5. **条件变量 (Condition)** - **原理**:基于的增强同步,支持`wait()`/`notify()` - **使用场景**:复杂状态依赖场景 - **示例**: ```python cond = threading.Condition() queue = [] def producer(): with cond: queue.append("数据") cond.notify() # 通知消费者 def consumer(): with cond: if not queue: cond.wait() # 等待通知 print(queue.pop()) ``` --- ### 三、的最佳实践 1. **优先使用`with`语句** 自动管理的获取/释放,避免忘记释放: ```python with lock: # 等价于 try-finally 管理 shared_data += 1 ``` 2. **避免死的策略** - 按固定顺序获取多个 - 设置获取超时:`lock.acquire(timeout=5)` - 使用`try-finally`确保释放 3. **性能优化** - 最小化作用域(减少内代码量) - 读写分离场景使用`RLock` - I/O密集型操作避免持 --- ### 四、线程在典型场景的应用 #### 1. 生产者-消费者模型 ```python import threading, queue buffer = queue.Queue(maxsize=10) lock = threading.Lock() def producer(): while True: with lock: if not buffer.full(): buffer.put("产品") print("生产产品") def consumer(): while True: with lock: if not buffer.empty(): item = buffer.get() print(f"消费{item}") ``` #### 2. 线程安全单例模式 ```python class Singleton: _instance = None _lock = threading.Lock() def __new__(cls): with cls._lock: if not cls._instance: cls._instance = super().__new__(cls) return cls._instance ``` --- ### 五、注意事项 1. **GIL的影响** Python的全局解释器(GIL)使线程在CPU密集型任务中效果有限,此时建议使用多进程。 2. **粒度选择** - 粗粒度:实现简单但并发性低 - 细粒度:并发性高但复杂度增加 > **关键原则**:应保护**最小必要共享资源**,而非整个线程逻辑[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值