zoukankan      html  css  js  c++  java
  • day 35 线程

    内容回顾

    # 互斥锁
        #在同一个进程中连续锁多次
    
    #进程的数据共享
        #进程之间可以共享数据
        #提供共享数据的类是Manager
        #但是它提供的list|dict 这些数据类型
            #针对+= -= *= -=
        #需要加锁保证安全
    
    # 进程之间的通信
    #     ipc:
            #queue
            #管道
    
        #机制也可以数据通信
        #manager
        #lock   #acquire  acquire 还有一块空间 有锁的状态
        #Process    #开启时
    
    # 进程锁  线程锁   #一个共享  一个锁就得通信  线程锁不锁得住 进程
    
    # 线程
        #概念
            #进程和线程的区别
                #进程  开销大  数据隔离
                    #是计算机中最小的资源分配单位
                #线程  轻量级  共享数据
                    #是计算机中能被cpu调度的最小单位
            #正常的线程是什么
                #能同时被多个cpu执行
    
            #Cpython解释器下的线程(解释器里有 锁只一个进程一个时刻会用)
                #GIL锁
                #全局解释器锁
                #是CPython解释其中的
                #会导致同一个时刻只能有一个线程
    
        #代码 threading 模块
            #Thread 类
                #开启线程
                #传参数
                #没有terminate
                #join
            #active_count   int  当前程序中正在执行的线程个数
            #current_thread    线程对象  能够获取 当前线程 的对象
    
    from multiprocessing import Process
    class Myprocess(Process):
        def __init__(self,args):
            super().__init__()
            self.args = args
        def run(self):
            print('子进程执行',self.name)
    
    p1 = Myprocess(1)
    p1.start()       #self指向p1  和p1 是一样的   #和 current_thread一样
    p1.join()
    
    
    p2 = Myprocess(1)
    p2.start()       #self指向p2  #水性杨花
    p2.join()
    View Code

    今日内容

    # 守护线程
    # threading.enumerate
    # 线程锁
    #互斥锁
    #递归锁
    #死锁现象
    #线程队列
    #进程池和线程池


    3 不大对的enumeate 方法
    #__author : 'liuyang' 
    #date : 2019/4/18 0018 上午 9:24
    from threading import Thread, enumerate,active_count
    def func():
        print('in son thread')
    Thread(target= func).start()
    print(enumerate())
    #[<_MainThread(MainThread, started 2788)>]
    active_count = len(enumerate())
    print(active_count)
    View Code

    老师讲

    from threading import enumerate,Thread
    
    def func():
        print('in son thread')
    
    Thread(target=func).start()
    print(enumerate()) # 返回一个存储着所有线程对象的列表
    # active_count = len(enumerate())
    # [<_MainThread(MainThread, started 1200)>,
    # <Thread(Thread-1, started 4156)>]
    View Code

    4守护线程

    #__author : 'liuyang' 
    #date : 2019/4/18 0018 上午 9:27
    from threading import Thread
    import time
    def daemon_func():
        while True:
            time.sleep(0.5)
            print('守护线程')
    
    def son_func():
        print("start son")
        time.sleep(5)
        print('end son')
    t = Thread(target=daemon_func)
    t.daemon = True
    t.start()
    Thread(target=son_func).start()
    time.sleep(3)
    print('主线程结束了')
    
    # 1. 主线程会等待子线程的结束而结束
    # 2. 守护线程会随着主线程的结束而结束
        #守护线程会守护主线程 和 所有的子线程
    
    
    # 进程会随着 主线程的结束而结束
        # 所有非守护线程终止,即使存在守护线程,进程运行终止
        # 所有守护线程终止,
    
    
    # 问题
        #1. 主线程需不需要回收子线程的资源
            #不需要  , 线程资源属于进程, 所以进程结束了,线程的资源自然就被回收了
        # 2 .主线程 为什么要等待子线程结束之后结束
            # 主线程结束意味着进程结束,所有的子线程都会结束
            # 要想让子线程能够顺利执行完,主线程只能等
        #3 守护线程 到底要怎么结束的
            #主线程结束了 进程也结束了 守护线程被主进程的结束结束掉了(守护了这个进程。。。)
    
    #守护进程: 只会守护到主进程的代码结束
    #守护线程: 会守护到 所有其它非守护线程的结束
    View Code

    5 线程

      还要加锁 ? GIL 的锁为了节省时间  没有很负责的锁住 浪费时间 只锁规定时间内(时间片)

    #__author : 'liuyang' 
    #date : 2019/4/18 0018 上午 10:10
    # 线程里有必要要锁么?
    # GIL 和 锁的关系        #轮流执行一个  cpu
    
    from dis import dis
    from threading import Thread,Lock
    
    count = 0
    # count = []
    l = []
    def add_func(lock):
        global count
        for i in range(200000):
            with lock :
                count += 1
            # count.append(1)
    def sub_func(lock):
        global count
        with lock:
            for i in range(200000):
                count -= 1
        # i = 200000
        # while i >0 :
        #     if count:
        #         count.pop()
        #         i -= 1
    
    def add_list():
        l.append(1)
    # dis(add_func)
    t_l = []
    lock = Lock()
    # for i in range(5):
    #     t1 =Thread(target=add_func,args = (lock,))
    #     t1.start()
    #     t_l.append(t1)
    #     t2 =Thread(target=sub_func,args= (lock,))
    #     t2.start()
    #     t_l.append(t2)
    # for t in t_l:t.join()
    # print(count)
    dis(add_list)
    # 数据不安全
    
    
    '''    两个线程模拟  加载count 1  加载+  存回全局变量
     14          14 LOAD_GLOBAL              1 (count)
                 16 LOAD_CONST               2 (1)
                 18 INPLACE_ADD
                 20 STORE_GLOBAL             1 (count)
                 22 JUMP_ABSOLUTE           10
            >>   24 POP_BLOCK
            >>   26 LOAD_CONST               0 (None)
                 28 RETURN_VALUE'''
    
    # 当执行 STORE         +=  两步操作
    # GIL  时间片 轮转 (700条 ):造成了数据不安全
        #锁保证一个时间短一个线程  但是 没执行完  没存值
            #不是python代码   而是底层cpu执行
    
    # l.append(1)
    '''     lst.append(1)  原子性:不可再分性      [1,1]
     30           0 LOAD_GLOBAL              0 (l)
                  2 LOAD_ATTR                1 (append)
                  4 LOAD_CONST               1 (1)
                  6 CALL_FUNCTION            1          # 调函数
                  8 POP_TOP                             #POP :出栈  #pop是弹出栈顶元素,top是获得栈顶元素,不弹出
                 10 LOAD_CONST               0 (None) 
                 12 RETURN_VALUE'''
    
    # if  [].pop()  #两步操作  轮转了  改变了 没法pop了 所以报错
    #    +=         #两步操作  轮转了   没改变全局变量存到里面  所以有重叠
    
    # 数据不安全
    #在线程中也会出现数据不安全的
        #1.对全局变量进行修改
        #2.对某个值 += -= *= /=
    # 通过加锁来解决
    
    # 设计都写好的函数  修改的
    # list pop append extend insert insert  remove
    # dict pop update
    # list[0] += 1
    # dic[key] -= 1
    
    #list.pop/append   # pop列表为空时会 报错
    # queue  put/get   # get队列为空时会 等待
    
    
    # GIL
    #
    # 如果没有GIL锁
    # append(1)   append(1)  多个cpu同时进行
    # 在一个列表地址中  被覆盖了
    
    # 1 . 有了GIL 保证了线程同一时刻只能有一个线程访问CPU,不可能有两个线程t同时在cpu上执行指令
    # 没有GIL操作  任何小操作都得加锁
    
    # 2 lock 锁 保证某一段代码 在没有执行完毕之后,不可能有另一个线程也执行它
    # 时间片轮转
    View Code

    6 科学家吃面问题

    两个人 两个东西 得二可得天下 但是一人一个

    #__author : 'liuyang' 
    #date : 2019/4/18 0018 上午 10:54
    #死锁: 永远解不开了才叫
    
    # acquire # acquire  低级的死锁 好开
    # '''
    from threading import Lock
    # 死锁代码  :操作两个变量的 两个函数同时使用两个变量  时机问题  我有叉子 你有面
                                                    # 自己代码没问题  没测到并发 很多线程  上线了服务
    from threading import Thread                   #用户一多  多线程  死锁 阻在那了 不走了
    noodle_lock = Lock()
    fork_lock = Lock()
    import time
    def eat1(name):
        noodle_lock.acquire()
        print('%s 拿到面了'%name)
        fork_lock.acquire()
        print('%s 拿到叉子'%name)
        print('%s 吃面'%name)
        time.sleep(0.1)
        fork_lock.release()
        print('%s 放下茶子'%name)
        noodle_lock.release()
        print('%s 放下面'%name)
    def eat2(name):
        fork_lock.acquire()
        print('%s 拿到叉子了'%name)
        noodle_lock.acquire()
        print('%s 拿到面了'%name)
        print('%s 吃面'%name)
        noodle_lock.release()
        print('%s 放下面' % name)
        fork_lock.release()
        print('%s 放下茶子'%name)
    
    Thread(target=eat1 , args=('alex',)).start()
    Thread(target=eat2 , args=('al',)).start()
    Thread(target=eat1 , args=('ex',)).start()
    
    # '''
     # 快速解决问题
                #改成一把锁
        #递归锁
        # 门口挂着一串(万能)钥匙 (互斥一个钥匙)
            #也是把两把锁并成 一把锁 ,别的结构不改
        # 很多个门
    
    #lock.acquire()
        #拿到面了
    #lock.acquire()
        #拿到叉子
        #吃面
    #lock.release()
        #放下面
    #lock.release()
        #放下叉子       #归还到最外层
    #
    # from threading import RLock , Lock
    # lock = Lock()
    # rlock = RLock()
    
    # lock.acquire()
    # print(123)
    # lock.acquire()
    # print(456)
    
    # rlock.acquire()
    # print(123)
    # rlock.acquire()
    # print(456)
    # rlock.acquire()
    # print(56)       #万能钥匙   可以近很多个锁门 但是得出来
    # rlock.release()
    # rlock.release()
    # rlock.release()
    
    # 搞两个锁都会锁 不管递归锁
    
    # 在同一个线程中是不会出现数据安全(死锁)  的 所以 递归锁 可以
    from threading import RLock , Lock,Thread
    # fork_lock = noodle_lock = RLock()
    # def eat1(name):
    #     noodle_lock.acquire()
    #     print('%s 拿到面了'%name)
    #     fork_lock.acquire()
    #     print('%s 拿到叉子'%name)
    #     print('%s 吃面'%name)
    #     fork_lock.release()
    #     print('%s 放下茶子'%name)
    #     noodle_lock.release()
    #     print('%s 放下面'%name)
    # def eat2(name):
    #     noodle_lock.acquire()
    #     print('%s 拿到叉子了'%name)
    #     fork_lock.acquire()
    #     print('%s 拿到面子'%name)
    #     print('%s 吃面'%name)
    #     fork_lock.release()
    #     print('%s 放下茶子'%name)
    #     noodle_lock.release()
    #     print('%s 放下面'%name)
    # Thread(target=eat1 , args=('l',)).start()
    # Thread(target=eat1 , args=('y',)).start()
    
    # lock = Lock()     #上面的效率低 改成  互斥锁
    # def eat1(name):       #最简单  没问题
    #     lock.acquire()
    #     print('%s 拿到面了'%name)
    #     print('%s 拿到叉子'%name)
    #     print('%s 吃面'%name)
    #     print('%s 放下茶子'%name)
    #     print('%s 放下面'%name)
    #     lock.release()
    # def eat2(name):
    #     lock.acquire()
    #     print('%s 拿到面了'%name)
    #     print('%s 拿到叉子'%name)
    #     print('%s 吃面'%name)
    #     print('%s 放下茶子'%name)
    #     print('%s 放下面'%name)
    #     lock.release()
    # Thread(target=eat1 , args=('l',)).start()
    # Thread(target=eat1 , args=('y',)).start()
    
    #两个进程 各拿到一个 钥匙
        #都完不成
    # 你要操作的数据不止一个   a = 1   b = 2   c = a+b
    
    # a.acquire()
        #忘了解锁   阻塞在某个点了  可能锁了
    # b.acquire()
    # a.release()
    # b.release()
    
    # 出错了 改不出来了  就先把所有的锁  改成一个锁   没问题,但是效率不高
    # 改好了, 再改一把把锁
    
    # 获取锁  释放锁   不要把大段代码 放了锁里
        #一把锁锁一个资源
    
    # 死锁不是锁
    
    #互斥锁
    #递归锁
    View Code

    7.队列

    #__author : 'liuyang' 
    #date : 2019/4/18 0018 下午 12:08
    from queue import Queue
    # Queue 就是一个线程队列的类, 自带 lock锁,实现了线程安全的数据类型
    # 队列是一个线程安全的数据类型  放进去拿出来
    
    # [][]+=1   不安全 列表
    q = Queue()
    q.put(1)
    
        #在多线程下都不准  异步没法控制
    q.empty() #判断是否为空
    q.full()  #判断是否为满
    q.qsize()  #队列的大小
    
    q.put({1,2,3})
    q.get()
    q.put_nowait({'abc'})
    print(q.get_nowait())   #先进先出
    print(q.get_nowait())
    
    #先进后出 的 队列 last in first out
    from queue import LifoQueue   #栈  后进先出的时候  都可以用过
    lfq = LifoQueue()
    lfq.put(1)
    lfq.put('abc')
    lfq.put({'1','2','3','4'})
    print(lfq.get())
    print(lfq.get())
    print(lfq.get())
    
    #  栈从空间复杂度上来讲  栈的效率要比递归高
    #  队列来讲顺序性
    
    from queue import PriorityQueue  #小的优先级高1   优先级队列
    pq = PriorityQueue()
    pq.put((1,'abcd'))
    pq.put((2,'dcba'))
    pq.put((20,'dddd'))
    print(pq.get())
    print(pq.get())
    print(pq.get())              #设置成会员 抢票好
    
    # 线程+队列 实现消费者生产者模型
    View Code

    8池 进程

    #__author : 'liuyang' 
    #date : 2019/4/18 0018 下午 12:08
    from queue import Queue
    # Queue 就是一个线程队列的类, 自带 lock锁,实现了线程安全的数据类型
    # 队列是一个线程安全的数据类型  放进去拿出来
    
    # [][]+=1   不安全 列表
    q = Queue()
    q.put(1)
    
        #在多线程下都不准  异步没法控制
    q.empty() #判断是否为空
    q.full()  #判断是否为满
    q.qsize()  #队列的大小
    
    q.put({1,2,3})
    q.get()
    q.put_nowait({'abc'})
    print(q.get_nowait())   #先进先出
    print(q.get_nowait())
    
    #先进后出 的 队列 last in first out
    from queue import LifoQueue   #栈  后进先出的时候  都可以用过
    lfq = LifoQueue()
    lfq.put(1)
    lfq.put('abc')
    lfq.put({'1','2','3','4'})
    print(lfq.get())
    print(lfq.get())
    print(lfq.get())
    
    #  栈从空间复杂度上来讲  栈的效率要比递归高
    #  队列来讲顺序性
    
    from queue import PriorityQueue  #小的优先级高1   优先级队列
    pq = PriorityQueue()
    pq.put((1,'abcd'))
    pq.put((2,'dcba'))
    pq.put((20,'dddd'))
    print(pq.get())
    print(pq.get())
    print(pq.get())              #设置成会员 抢票好
    
    # 线程+队列 实现消费者生产者模型
    View Code

    9总结

    # 守护线程
    #锁
        #互斥
        #递归
        #死锁现象
    
    #队列  线程安全的数据
        #先进先出  queue
        #后进先出   LifoQueue
        #优先级队列  PriorityQueue
    
    #池
        #控制进程的数量
        #节省资源开销
  • 相关阅读:
    IntelliJ IDEA 设置代码提示或自动补全的快捷键
    spring框架学习(一)
    java数据库连接池技术原理(浅析)
    Spring事务管理
    Android invalidate 用法
    sharedPreferences存储文件
    Android Service组件
    SQLite版本升级
    在android.app.Application中定义全局变量 .
    android观察者模式
  • 原文地址:https://www.cnblogs.com/Doner/p/10729084.html
Copyright © 2011-2022 走看看