# 环境
- windows7
- anaconda
- pycharm3.6
# 多线程 vs 多进程
- 程序:一堆代码以文本形式存入一个文档
- 进程:程序运行的一个状态
- 包含地址空间、内存、数据栈等
- 每个进程有自己完全独立的运行环境,此时如果多进程想要共享数据就会遇到困难
- 线程:一个进程的独立运行片段,一个进程可以有多个线程
- 轻量化的进程
- 一个进程的多个线程之间共享数据和上下文环境
- 共享互斥问题
- 全局解释器锁(GIL)
- Python代码的执行是由python虚拟机进行控制
- 在主循环中只能有一个控制线程在执行
- Python包
- thread:有问题,不好用,python3改成了_thread
- 案例01.py:顺序执行。耗时较长
- 案例02.py:改用多线程,缩短总用时,使用的是_thread
- 案例03.py: _thread,传参数

- threading:现在用的
- 直接利用threading.Thread生成Thread实例
1、t = threading.Thread(target=xxx, args=(xxx,))
2、t.start() 启动多线程
3、t.join() 等待多线程执行完成
4、案例04.py
- 守护线程 daemon
- 如果在程序中将子线程设置成守护线程,则这个子线程会在主线程结束的时候自动退出
- 一般认为,守护线程不重要或者不允许离开主线程独立运行
- 守护线程案例能否有效果跟环境相关
- 案例06.py 非守护线程
- 案例07.py 守护线程

- 线程常用属性:
- threading.currentThread:返回当前线程变量
- threading.enumerate:返回一个包含正在运行的线程的list,正在运行的线程指的是线程启动后,结束前
- threading.activeCount:返回正在运行的线程数量,效果跟len(threading.enumerate)相同
- thr.setName:给线程设置名字
- thr.getName:得到线程名字
线程常用属性案例08.py
- 直接继承自threading.Thread
- 直接继承Thread
- 重写run函数
- 类实例可以直接运行
- 案例09.py
- 案例10.py 企业常用,工业风写法
- 共享变量
- 共享变量:当多个线程同时访问一个变量的时候,由于不是原子操作,导致会产生共享变量冲突
- 案例11.py

- 解决变量方案:锁、信号灯
- 锁(Lock):
- 是一个标志,表示一个线程在占用一些资源
- 使用过程:
- 上锁
- 放心使用共享资源
- 取消锁、释放锁
- 案例12.py
- 锁谁:哪个资源需要多个线程共享,就锁那个
- 理解锁:锁其实不是锁住谁,而是一个令牌
- 线程安全问题:
- 如果一个资源/变量,它对于多线程来讲,不用加锁也不会引起任何问题,则称为线程安全
- 线程不安全变量类型:list,set,dict
- 线程安全变量类型:queue队列

- 生产者消费者问题
- 一个模型,可以用来搭建消息队列
- queue是一个用来存放变量的数据结构,特点是先进先出,内部元素排队,可以理解成一个特殊的list
import threading import time import queue # Python2 # from Queue import Queue # Python3 # import queue # 模拟生产者Producer类 class Producer(threading.Thread): def run(self): global q count = 0 while True: # queue.qsize()返回queue内容长度 if q.qsize() < 1000: for i in range(100): count = count +1 msg = '生成产品'+str(count) # put是往queue放入一个值 q.put(msg) print(msg) time.sleep(0.5) # 模拟消费者Consumer class Consumer(threading.Thread): def run(self): global q while True: if q.qsize() > 100: for i in range(3): # q.get()是从queue中取出一个值 msg = self.name + "消费了" + q.get() print(msg) time.sleep(1) if __name__ == "__main__": q = queue.Queue() for i in range(500): q.put('初始产品'+str(i)) for i in range(2): p = Producer() p.start() for i in range(5): c = Consumer() c.start()
# 死锁问题 # 下面的例子讲的是:t1和t2两个线程同时跑func_1和func_2,func_1申请了lock_1之后睡了两秒,func_2申请了lock_2后睡了4秒,
# func_1睡醒后要申请lock_2,func_2睡醒后要申请lock_1,但此时双方都未释放之前各自的申请锁,导致出现死锁 # 打印结果如下 ''' 主程序启动... func_1 starting... func_1 申请了 lock_1... func_2 starting... func_2 申请了 lock_2... func_1 等待2秒后执行 lock_2 申请... func_2 等待4秒后执行 lock_1 申请... ... import threading import time lock_1 = threading.Lock() lock_2 = threading.Lock() # 申请锁和释放锁的顺序相反 def func_1(): print("func_1 starting...") lock_1.acquire() print("func_1 申请了 lock_1...") time.sleep(2) print("func_1 等待2秒后执行 lock_2 申请...") lock_2.acquire() print("func_1 申请了 lock_2...") lock_2.release() print("func_1 释放了 lock_2") lock_1.release() print("func_1 释放了 lock_1") print("func_1 done...") def func_2(): print("func_2 starting...") lock_2.acquire() print("func_2 申请了 lock_2...") time.sleep(4) print("func_2 等待4秒后执行 lock_1 申请") lock_1.acquire() print("func_2 申请了 lock_1...") lock_1.release() print("func_2 释放了 lock_1...") lock_2.release() print("func_2 释放了 lock_2...") print("func_2 done...") if __name__ == "__main__": print("主程序启动...") t1 = threading.Thread(target=func_1, args=()) t2 = threading.Thread(target=func_2, args=()) t1.start() t2.start() t1.join() t2.join() print("主程序结束...")
# 死锁问题解决方法1:根据申请锁的等待时间timeout延长申请时间,之后获取其返回值判断成功与否 ''' 主程序启动... func_1 starting... func_1 申请了 lock_1... func_2 starting... func_2 申请了 lock_2... func_1 等待2秒后执行 lock_2 申请... func_2 等待4秒后执行 lock_1 申请... func_1 等了两秒也没申请到 lock_2... func_1 释放了 lock_1 func_1 done... func_2 申请了 lock_1... func_2 释放了 lock_1... func_2 释放了 lock_2... func_2 done... 主程序结束... ''' import threading import time lock_1 = threading.Lock() lock_2 = threading.Lock() # 申请锁和释放锁的顺序相反 # 假定func_1肯定能申请到lock_1 # 假定func_2肯定能申请到lock_2 def func_1(): print("func_1 starting...") lock_1.acquire(timeout=4) print("func_1 申请了 lock_1...") time.sleep(2) print("func_1 等待2秒后执行 lock_2 申请...") # 申请锁2返回值,成功True rst = lock_2.acquire(timeout=2) if rst: print("func_1 已经得到锁 lock_2...") lock_2.release() print("func_1 释放了 lock_2...") else: print("func_1 等了两秒也没申请到 lock_2...") lock_1.release() print("func_1 释放了 lock_1") print("func_1 done...") def func_2(): print("func_2 starting...") lock_2.acquire() print("func_2 申请了 lock_2...") time.sleep(4) print("func_2 等待4秒后执行 lock_1 申请...") lock_1.acquire() print("func_2 申请了 lock_1...") lock_1.release() print("func_2 释放了 lock_1...") lock_2.release() print("func_2 释放了 lock_2...") print("func_2 done...") if __name__ == "__main__": print("主程序启动...") t1 = threading.Thread(target=func_1, args=()) t2 = threading.Thread(target=func_2, args=()) t1.start() t2.start() t1.join() t2.join() print("主程序结束...")
# 死锁问题的解决方法2:semaphore
- 允许一个资源最多可以几个线程同时使用,信号灯的意思
- 案例16.py
- threading.Timer 定时器
- Timer是利用多线程,在指定时间后启动一个功能
- 案例17.py

- 可重入锁
- 一个锁,可以被一个线程多次申请
- 主要解决递归调用的时候,需要申请锁的情况
- 案例18.py