zoukankan      html  css  js  c++  java
  • 线程

    相关概念

    线程:是轻量级(轻型)进程,比进程开销小得多

    进程和线程的关系:

    线程是进程的一部分,线程必须依赖于进程存在

    一个进程之内至少有一条线程,默认存在的线程叫做主线程,也可以在一个进程中开启多个子线程来完成更多的任务

    进程是计算机中最小的资源分配单位

    线程是计算机中能被CPU调度的最小单位,是计算机中最小的执行单位

    进程负责分配资源和资源隔离,线程负责真正的执行代码

    正常进程与线程之间的区别:

    进程: 数据隔离,开销大,能利用多核

    线程: 数据共享,开销小,能利用多核

     python中进程与线程的区别:

    进程: 数据隔离,开销大,能利用多核

    线程: 数据共享,开销小,不能利用多核(GIL全局解释器锁)

    全局解释器锁:

    锁的是线程,让同一个进程中的多个线程同一时间只能由一个被CPU调度

     threading模块

    import os
    import time
    from threading import Thread
    from multiprocessing import Process
    # multiprocessing完全仿照了threading模块来进行封装
    def func(i):
        # print('start',i)
        i+=1
        # print(i,os.getpid())
    
    if __name__ == '__main__':
        start = time.time()
        for i in range(20):
            Thread(target=func,args=(i,)).start()
        print(time.time() - start)
        start = time.time()
        for i in range(20):
            Process(target=func,args=(i,)).start()
        print(time.time() - start)

    数据共享

    from threading import Thread
    n = 100
    def func():
        global n
    
        n -= 1
    t_l = []
    for i in range(100):
        t = Thread(target=func)
        t.start()
        t_l.append(t)
    for t in t_l:
        t.join()
    print(n)     #此时打印n为0,主线程中的值被改变

    使用函数的方法启动线程

    import time
    from threading import Thread,currentThread
    def func():
        time.sleep(1)
        print('in func:',currentThread().name,currentThread().ident)    #在函数内部获取当前前程名和线程id
    
    t = Thread(target=func)
    t.start()
    print(t.name)           # 在函数外部获取线程名和线程id
    print(t.ident)

    使用类的方法启动线程

    import time
    from threading import Thread
    class MyThread(Thread):
        def __init__(self,i):
            self.i = i
            super().__init__()
        def run(self):
            time.sleep(1)
            print('in MyThread:',self.i,self.name,self.ident)
            #self.name  线程名   self.ident  线程id
    
    for i in range(10):
        t = MyThread(i)
        t.start()

    active_count和enumerate

    import time
    from threading import Thread,active_count,enumerate
    def func():
        time.sleep(1)
        print('in func')
    
    t = Thread(target=func)
    t.start()
    print(enumerate())  # 当前所有活着的线程对象组成的列表  内存地址
    print(active_count()) #统计当前所有或者的线程对象的格式  相当于len(enumerate)

    守护线程

    守护进程 会等到主进程的代码结束而结束

    守护线程 会等待主线程的结束而结束,如果主线程还开启了其他子线程,那么守护线程会守护到最后

    线程是数据进程的,如果进程结束了,线程也就结束了

    import time
    from threading import Thread
    def func():
        while True:
            time.sleep(1)
            print('in func')
    
    def son():
        print('son start')
        time.sleep(8)
        print('son end')
    
    t1 = Thread(target=func)
    t1.setDaemon(True)   # 设置守护状态
    t1.start()
    t2 = Thread(target=son)
    t2.start()
    time.sleep(5)      # 守护进程会等到子进程8秒之后运行结束再结束 

    数据安全问题

    列别/字典 自带的方法基本都是数据安全的,但是对其中的元素进行 +=  -=  *=  /= 都是数据不安全的

    由于append等方法是一个完整的指令过程,执行了就相当于添加,不执行就是还没添加

    而+=这种方法,是先执行+,再对结果进行赋值,如果执行完+之后,时间片就切换到另一个线程了,那么就会出现数据不安全的情况

    互斥锁

    通过加锁的方式,保证+=执行完毕后,再释放锁让其他线程能够执行

    from threading import Lock,Thread
    import time
    
    def work(lock):
        global n
        with lock:
            temp = n
            time.sleep(0.1)
            n = temp - 1
    
    if __name__ == '__main__':
        n = 100
        l = []
        lock = Lock()                  #互斥锁
        for i in range(100):
            p = Thread(target=work,args=(lock,))
            p.start()
            l.append(p)
        for p in l:
            p.join()
        print(n)

    死锁现象

    科学家吃面问题(对叉子和面分别上锁,同时规定一个人必须同时拿到叉子和面才可以吃到面)

    import time
    from threading import Lock,Thread
    noodle_lock = Lock()      # 对面加锁
    fork_lock = Lock()        # 对叉子加锁
    fork = 10
    noodle = 20
    def eat1(name):
        noodle_lock.acquire()
        print('%s拿到面了'%name)
        fork_lock.acquire()
        print('%s拿到叉子了' % name)
        noodel =fork + noodle
        time.sleep(0.1)
        print('%s吃到面了'%name)
        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)
        noodel =fork + noodle
        time.sleep(0.1)
        print('%s吃到面了'%name)
        noodle_lock.release()
        print('%s放下面了' % name)
        fork_lock.release()
        print('%s放下叉子了' % name)
    
    
    Thread(target=eat1,args=('yuan',)).start()
    Thread(target=eat2,args=('alex',)).start()
    Thread(target=eat1,args=('wusir',)).start()
    Thread(target=eat2,args=('taibai',)).start()
    # 会出现一个人拿到叉子,一个人拿到面的情况,出现死锁现象

    递归锁--快速解决死锁现象

    import time
    from threading import RLock,Thread
    fork_lock = noodle_lock = RLock()     #将面和叉子统一加一个递归锁
    fork = 10
    noodle = 20
    def eat1(name):
        noodle_lock.acquire()
        print('%s拿到面了'%name)
        fork_lock.acquire()
        print('%s拿到叉子了' % name)
        noodel =fork + noodle
        time.sleep(0.1)
        print('%s吃到面了'%name)
        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)
        noodel =fork + noodle
        time.sleep(0.1)
        print('%s吃到面了'%name)
        noodle_lock.release()
        print('%s放下面了' % name)
        fork_lock.release()
        print('%s放下叉子了' % name)
    
    Thread(target=eat1,args=('yuan',)).start()
    Thread(target=eat2,args=('alex',)).start()
    Thread(target=eat1,args=('wusir',)).start()
    Thread(target=eat2,args=('taibai',)).start()

    用互斥锁将两个资源当成一个资源锁起来,效率会更高,在实际开发中更推荐用互斥锁

    import time
    from threading import Lock,Thread
    lock = Lock()           # 只用一个锁来锁资源
    fork = 10
    noodle = 20
    def eat1(name):
        lock.acquire()
        print('%s拿到面了'%name)
        print('%s拿到叉子了' % name)
        noodel =fork + noodle
        time.sleep(0.1)
        print('%s吃到面了'%name)
        print('%s放下叉子了' % name)
        print('%s放下面了'%name)
        lock.release()
    
    def eat2(name):
        lock.acquire()
        print('%s拿到叉子了' % name)
        print('%s拿到面了'%name)
        noodel =fork + noodle
        time.sleep(0.1)
        print('%s吃到面了'%name)
        print('%s放下面了' % name)
        print('%s放下叉子了' % name)
        lock.release()
    
    
    Thread(target=eat1,args=('yuan',)).start()
    Thread(target=eat2,args=('alex',)).start()
    Thread(target=eat1,args=('wusir',)).start()
    Thread(target=eat2,args=('taibai',)).start()

    总结:

    死锁现象是由于多个锁对多个变量管理不当造成的

    互斥锁在一个进程内只能被acquire一次

    递归锁在一个进程内可以被acquire多次,但是release个数必须和acquire个数相同

    线程队列

    队列  先进先出   FIFO

    栈     后进先出    LIFO

    import queue
    lifoq = queue.LifoQueue()   #
    lifoq.put(1)
    lifoq.put(2)
    lifoq.put(3)
    print(lifoq.get())
    print(lifoq.get())
    print(lifoq.get())
    
    结果:
    3
    2
    1

    优先级队列

    import queue
    pq = queue.PriorityQueue()
    pq.put((2,'wusir'))
    pq.put((1,'yuan'))
    pq.put((1,'alex'))
    pq.put((3,'taibai'))
    print(pq.get())
    print(pq.get())
    print(pq.get())
    print(pq.get())
    
    结果:
    # 按照优先级,如果第一个相同,会根据第二个元素的ascii码的顺序排序
    (1, 'alex')
    (1, 'yuan')
    (2, 'wusir')
    (3, 'taibai')

    线程池

    oncurrent.futures      模块提供了高度封装的异步调用接口
    ThreadPoolExecutor     线程池,提供异步调用
    ProcessPoolExecutor    进程池,提供异步调用
    方法:
    submit     异步提交任务
    map        代替for循环中submit的操作
    shutdown   相当于进程池中close()+join()操作
    result     取得结果
    add_done_callback  回调函数
    done       判断某一个线程是否完成
    cancel     取消某个任务
    import time
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    def func(i):
        time.sleep(1)
        print('in son thread',i)
    
    if __name__ == '__main__':
        tp = ThreadPoolExecutor(4)   # 开启4个线程池
        # tp.map(func,range(20))     # 可以使用map,也可以使用for循环+submit提交任务
        for i in range(20):
            tp.submit(func,i)
        tp.shutdown()
        print('所有的任务都执行完了')
    示例

    获取返回值

    import time
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    def func(i):
        time.sleep(1)
        return '*'* i
    
    if __name__ == '__main__':
        tp = ThreadPoolExecutor(4)   #开启4个线程池
    
        # 使用map提交任务,获取返回值
        # ret_l = tp.map(func,range(20))
        # for ret in ret_l:
        #     print(ret)
    
        #使用submit提交任务,获取返回值
        ret_l = []
        for i in range(20):
            ret = tp.submit(func,i)
            ret_l.append(ret)
        for ret in ret_l:
            print(ret.result())

    回调函数

    import time
    import random
    from concurrent.futures import ThreadPoolExecutor
    
    def back(ret):
        print(ret.result())
    def son_func(i):
        time.sleep(random.random())
        return i ** i
    
    tp = ThreadPoolExecutor(4)
    for i in range(20):
        ret = tp.submit(son_func,i)    
        ret.add_done_callback(back)    
  • 相关阅读:
    vbs获取当月的第一天和最后一天的日期
    vbscript基础篇
    win10专业版激活
    python selenium中Excel数据维护
    python里面的xlrd模块详解
    python 转换为json时候 汉字编码问题
    用VBA得到EXCEL表格中的行数和列数
    表关联关系,表的复制
    存储引擎,详细建表语句,数据类型,约束
    数据库基础
  • 原文地址:https://www.cnblogs.com/sandy-123/p/10461610.html
Copyright © 2011-2022 走看看