学习笔记-Python基础16-多线程

# 环境
- 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

    
原文地址:https://www.cnblogs.com/Cloudloong/p/9773784.html