• 线程


    一.学习线程

    1.线程的概念

      线程被称作轻量级的进程

      计算机的执行单位以线程为单位

      计算机的最小可执行是线程

      进程是资源分配的基本单位,线程是可执行的基本单位,是可被调度的基本单位

      线程不可以自己独立拥有资源,线程的执行必须依赖于所属进程中的资源,进程中必须至少应该有一个线程

    2.线程的组成

      线程是由代码段,数据段,TCB(thread control block)线程管理控制组成

    3.GIL锁

      GIL是全局解释锁,只有CPython解释器才会有,限制只能由一个线程来访问CPU

      对于线程来说,因为有了GIL锁,所以没有真正的并行

      GIL是锁线程的,意思是在同一时间内只允许一个线程访问CPU

    4.线程的分类(了解)

      线程分为用户级线程和内核级线程

      用户级线程:对于程序员来说,这样的线程完全被程序员控制执行调度

      内核级线程:对于计算机内核来说,这样的线程完全被内核控制调度

    5.线程的基本代码

    thread      ----    线程

    import Thread     ----    操作线程的模块

    import threading import Thread    常用这个去操作线程

    例如:普通的线程

    from threading import Thread
    import time

    def func():
    time.sleep(1)
    print("这是一个子线程")


    t = Thread(target=func,args=())
    t.start()
    print("这是主线程")

    例如:继承的方式(继承Thread类)

    from threading import Thread
    import time

    class MyThread(Thread):
    def __init__(self):
    super(MyThread, self).__init__()
    def run(self):
    time.sleep(2)
    print("这是一个子线程")

    t = MyThread()
    t.start()
    print("这是父进程")

    二.线程和进程的比较

    1.CPU切换进程要比CPU切换线程慢很多

      在Python中,如果IO操作过多的话,使用多线程是最好的

    例如:

    from threading import Thread
    from multiprocessing import Process
    import time

    def func():
    pass

    if __name__ == '__main__':
    start = time.time()
    for i in range(100):
    p = Process(target=func,args=())
    p.start()
    print("进程的运行速度:",time.time() - start)

    if __name__ == '__main__':
    start = time.time()
    for i in range(100):
    t = Thread(target=func,args=())
    t.start()
    print("线程的运行速度:",time.time() - start)

    2.在同一个进程内,所有的线程共享这个进程的pid,也是就说所有线程共享所属进程的所有资源和内存地址

    例如:

    from threading import Thread
    from multiprocessing import Process
    import os

    def func():
    print("子进程的pid值:%s" % os.getpid())

    def func1():
    print('子线程的pid值:%s' % os.getpid())

    if __name__ == '__main__':
    p = Process(target=func,args=())
    p.start()
    print("父进程的pid值:%s" % os.getpid())

    if __name__ == '__main__':
    t = Thread(target=func1,args=())
    t.start()
    print("父线程的pid值:%s" % os.getpid())

    打印结果:

    3.在同一个进程内,所有线程共享该进程中的全局变量

    例如:

    from threading import Thread

    def func():
    global num
    num -= 1

    if __name__ == '__main__':
    num = 100
    l = []
    for i in range(100):
    t = Thread(target=func,args=())
    t.start()
    l.append(t)
    [t.join() for t in l]
    print(num) =====> 0

    4.因为有GIL锁的存在,在Cpython中,没有真正的线程并行,但是有真正的多进程并行

      当你的任务是计算密集的情况,使用多进程好

    例如:

    from threading import Thread
    import time

    def func():
    global num #调用主线程的全局变量
    tmp = num
    time.sleep(0.001)
    num = tmp - 1

    if __name__ == '__main__':
    num = 100
    l = []
    for i in range(100):
    t = Thread(target=func,args=())
    t.start()
    l.append(t)
    [t.join() for t in l] ====>异步开启一百个线程
    print(num)

    总结:在Cpython中,IO密集用多线程,计算密集用多进程

    5.关于守护进程和守护线程的事情(注意:代码执行结束并不代表程序结束)

      守护进程:要么自己正常结束,要么根据父进程的代码执行结束而结束

      守护线程:要么自己正常结束,要么根据父线程的执行结束而结束

    例如:守护线程

    from threading import Thread
    import time

    def func():
    time.sleep(3)
    print(123)

    if __name__ == '__main__':
    t = Thread(target=func,args=())
    t.daemon = True # 设置子线程为守护线程
    t.start()
    time.sleep(2)

    例如:守护进程

    from multiprocessing import Process
    import time

    def func():
    time.sleep(2)
    print(123)

    if __name__ == '__main__':
    p = Process(target=func,args=())
    p.daemon = True #设置子进程为守护进程
    p.start()
    print(456)

    守护线程:

      守护线程是根据主线程执行结束才结束

      守护线程不是根据主线程的代码执行结束而结束

      主线程会等待普通线程执行结束再结束

      守护线程会等待主线程结束再结束

      所以,一般把不重要的事情设置为守护线程

    
    

     三.线程的使用方法

     1.锁机制   (他们锁的都是数据)

      RLOCK:递归锁,是无止境的锁,但是所有的锁有一个共同的钥匙

      LOCK:互斥锁,一把钥匙一把锁 ,用于保护数据安全

           共享资源,又叫做临界资源,共享代码,又叫做临界代码

           对临界资源进行操作时,一定要加锁

     例如:LOCK互斥锁

    from threading import Thread,Lock

    def func():
    l.acquire() #一把钥匙一个锁
    print(123)
    l.release()


    if __name__ == '__main__':
    l = Lock()
    t = Thread(target=func,args=())
    t1 = Thread(target=func,args=())
    t.start()
    t1.start()

     

    例如:RLOCK递归锁(开了几个门,就还几次钥匙)

    第一种情况:在同一个线程内,递归锁可以无止尽的acquire,但是互斥锁不行

    第二种情况:在不同的线程内,递归锁是保证只能被一个线程拿到钥匙,然后无止尽的acquire,其它线程等待

    from threading import Thread,RLock

    def func():
    l.acquire() #拿了一把钥匙,开了四个门
    l.acquire()
    l.acquire()
    l.acquire()
    print(123)
    l.release() #需要将四个门都打开,其它人才能拿了钥匙开门进入
    l.release()
    l.release()
    l.release()

    if __name__ == '__main__':
    l = RLock()
    t = Thread(target=func,args=())
    t.start()
    t1 = Thread(target=func,args=())
    t1.start()

     

    2.多线程的信号量

    from threading import Semaphore 

    例如:

    from threading import Thread,Semaphore
    import time,random

    def func(i,s):

    s.acquire() #拿了钥匙,锁了门
    print("33[34m 第%s个人进入小黑屋,拿了钥匙 33[0m" % i)
    time.sleep(random.randint(1,3)) #随机数,随机几秒出门
    print("33[33m 第%s个人出去小黑屋,还了钥匙 33[0m" % i)
    s.release() #还了钥匙,开了门

    if __name__ == '__main__':
    s = Semaphore(5) #初始化5把钥匙,也就是说允许5个人同时进入,之后其它人必须在门外等待,有人出来,他们拿了钥匙才能允许进入
    for i in range(20):
    t = Thread(target=func,args=(i,s))
    t.start()

     

    3.多线程的事件

    from threading import Event

    例如:

    from threading import Thread,Event
    import time

    def light(e):
    while 1:
    if e.is_set(): (if e.is_set() = True) #如果为True的时候,绿灯亮
    time.sleep(5) #绿灯亮五秒钟,此时可以通车
    print("33[31m 红灯亮 33[0m") #五秒钟之后,变为红灯
    e.clear() #将e.is_set()的bool值变为False
    else:
    time.sleep(5) #此时红灯亮五秒钟
    print("33[33m 绿灯亮 33[0m") #五秒钟之后,变为绿灯
    e.set() #将e.is_set()的bool值变为True

    def car(e,i):
    e.wait() #此时判断e.is_set()的bool值是True还是False
    print("第%s辆车通过路口" % i) #如果是True,则绿灯亮,让通车 ,如果是False,则是红灯.不让通车

    if __name__ == '__main__':
    e = Event() #实例化一个事件
    t = Thread(target=light,args=(e,))
    t.start()
    for i in range(50): #描述50辆车的进程
    c = Thread(target=car,args=(e,i+1))
    c.start()

     

    4.多线程的条件

     from threading import Condition

      条件是让程序员自行去调度线程的一个机制

     Conditon涉及的4个方法

    (1)acquire()   拿钥匙,锁门

    (2)release()   还钥匙,开门

    (3)wait()        是指让线程阻塞住

    (4)notify(int)  是指给wait发一个信号,让wait变成不阻塞

      int是指      你要给多少,给wait发信号

    例如:

    from threading import Thread,Condition

    def func(con,i):
    con.acquire() #拿了钥匙,锁了门
    con.wait() #让线程阻塞住
    con.release() #还了钥匙,开了门
    print("第%s个线程开始执行了" % i) #开始执行线程

    if __name__ == '__main__':
    con = Condition()
    for i in range(10):
    t = Thread(target=func,args=(con,i))
    t.start()
    while 1:
    num = int(input(">>>>")) #循环输入一个数字
    con.acquire() #拿了钥匙,锁了门 (主线程和10个子线程都在抢夺递归锁的一把钥匙,如果主线程抢到钥匙,主线程执行while循环,input,然后notify发信号,还钥匙,但是如果主线程执行速度特别快,极有可能接下来主线程又会拿到钥匙,那么此时哪怕其它10个子线程的wait接收到信号,但是因为没有拿到钥匙,所以其它子线程还是不会执行)
    con.notify(num) #给wait发输入的数字个信号
    con.release() #还了钥匙,开了门

    5.线程的定时器 

     from threading import Timer

    Timer(time,func)

    time:睡眠时间,以秒为单位

    func:睡眠时间过后,需要执行的任务(函数)

    例如:

    from threading import Timer
    def func():
    print(123)

    Timer(5,func).start() #将该方法睡眠五秒之后再执行

    6.多线程的守护线程

      守护线程是根据主线程执行结束才结束

      守护线程不是根据主线程的代码执行结束而结束

      主线程会等待普通线程执行结束再结束

      守护线程会等待主线程结束再结束

      所以,一般把不重要的事情设置为守护线程

     例如:

    from threading import Thread
    import time

    def func():
    time.sleep(3) #让守护线程睡3秒之后在执行
    print(123) #此时打印不出来123

    if __name__ == '__main__':
    t = Thread(target=func,args=())
    t.daemon = True #将子线程设置为守护线程
    t.start()
    time.sleep(2) #让主线程睡2秒之后执行结束,所以两秒之后主线程执行结束后,守护线程也就跟着结束,如果守护线程睡眠时间比主线程睡眠时间短,则守护线程会执行完

    7.同一进程内的队列

    import queue   适用于同一个进程内的队列,不能做多进程之间的通信

    from multiprocessing import Queue  用于多进程的队列,就是专门用来作进程之间的通信(IPC)

    (1)先进先出队列

    import queue

    q = queue.Queue()
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get()) ====> 1
    print(q.get()) ====> 2

    (2)后进先出队列

    import queue

    q = queue.LifoQueue()
    q.put(5)
    q.put(4)
    q.put(3)
    print(q.get()) ====> 3
    print(q.get()) ====> 4

    (3)优先级队列

    例子一:

    import queue

    q = queue.PriorityQueue()
    q.put((1,"123"))
    q.put((2,"456"))
    q.put((3,"789"))
    print(q.get()) =====> (1,"123")
    print(q.get()) =====> (2,"456")

    例子二:

    import queue

    q = queue.PriorityQueue()
    q.put(("b",123))
    q.put(("c",456))
    q.put(("a",123))
    print(q.get()) ====> ("a",123)
    print(q.get()) ====> ("b",123)

    例子三:

    import queue

    q = queue.PriorityQueue()
    q.put(("中国",123))
    q.put(("美国",456))
    q.put(("韩国",789))
    print(q.get()) ====> ("中国",123)

    优先级队列:首先保证整个队列中,所有表示优先级的东西类型必须一致

                  put方法接收的是一个元组类型(),第一个位置是优先级,第二个位置是数据

          优先级如果是数字,直接比较数值

          字符串比较的话,会先比较第一位的ASCII码,如果相等的话继续向下比较

          当ASCII码相同时,会按照先进先出的原则

    8.线程池

    线程池:在一个池子里,放固定数量的线程,这些线程等待任务,一旦有任务来,就有线程去执行

    from concurrent.futures import ThreadPoolExecutor    (线程池)

    from concurrent.futures import ProcessPoolExecutor     (进程池)

    concurrent.futures       ====> 这个模块是异步调用的机制,提交的任务都是用submit

    for + submit   多任务提交

    shutdown   ====> 是等效于Pool中的close + join,是指不允许在继续向池中增加任务,然后让父进程(线程)等待池中所有进程执行完所有任务

    (1)线程池和进程池的效率比较

    例子一:异步调用的线程池

    from concurrent.futures import ThreadPoolExecutor
    import time

    def func(num):
    sum = 0
    for i in range(num):
    sum += i ** 2
    # print(sum)

    if __name__ == '__main__':
    t = ThreadPoolExecutor(20)
    start = time.time()
    for i in range(1000):
    t.submit(func,i)
    t.shutdown()
    print("异步线程池执行的时间:",time.time() - start)

    例子二:异步调用的进程池

    from concurrent.futures import ProcessPoolExecutor
    import time

    def func(num):
    sum = 0
    for i in range(num):
    sum += i ** 2
    # print(sum)

    if __name__ == '__main__':
    p = ProcessPoolExecutor(5)
    start = time.time()
    for i in range(1000):
    p.submit(func,i)
    p.shutdown()
    print("异步进程池执行的时间:",time.time() - start)

    例子三:异步进程池

    from multiprocessing import Pool
    import time

    def func(num):
    sum = 0
    for i in range(num):
    sum += i ** 2
    # print(sum)

    if __name__ == '__main__':
    p = Pool(5)
    start = time.time()
    for i in range(1000):
    res = p.apply_async(func,i)
    p.close()
    p.join()
    print("进程池异步执行时间:",time.time() - start)

    总结:

      不管是Pool的进程池还是ProcessPoolExecutor()的进程池,他俩执行效率相当

      ThreadPoolExecutor()的效率要差很多

      所以当计算密集的时候,使用多线程

    (2)线程池中的方法

    ① map()    

    多任务的提交的两种方法

    要么直接用for + submit 的方式去提交多个任务

    要么直接使用map的方式提交任务,结果是一个生成器,采用__next__()的方式去拿结果

    例如一:

    from concurrent.futures import ThreadPoolExecutor
    import time

    def func(num):
    sum = 0
    for i in range(num):
    sum += i ** 2
    # print(sum)

    t = ThreadPoolExecutor(20)
    start = time.time()
    t.map(func,range(1000)) =====> 提交多个任务给池中,等效于for循环 + submit
    t.shutdown()
    print(time.time() - start)

    例如二:

    from concurrent.futures import ThreadPoolExecutor
    import time

    def func(num):
    sum = 0
    for i in range(num):
    sum += i ** 2
    # print(sum)

    t = ThreadPoolExecutor(20)
    start = time.time()
    for i in range(1000):
    t.submit(func,i)
    t.shutdown()
    print(time.time() - start)

    ②多任务的返回值

    例如一:

    from concurrent.futures import ThreadPoolExecutor

    def func(num):
    sum = 0
    for i in range(num):
    sum += i ** 2
    return sum

    t = ThreadPoolExecutor(20)
    # l = []
    for i in range(1000):
    res = t.submit(func,i)
    print(res.result())
    # l.append(res)
    # [print(i.result()) for i in l]

    例如二:

    from concurrent.futures import ThreadPoolExecutor

    def func(num):
    sum = 0
    for i in range(num):
    sum += i ** 2
    return sum

    t = ThreadPoolExecutor(20)
    res = t.map(func,range(1000))
    t.shutdown()
    # for i in res: =====> res 本身是个生成器(迭代器) 可以通过for循环拿值 也可以通过抛异常拿值
    # print(i)
    while 1:
    try:
    print(res.__next__())
    except StopIteration:
    break

    ③进程池的回调函数

    from concurrent.futures import ThreadPoolExecutor

    def func(num):
    sum = 0
    for i in range(num):
    sum += i ** 2
    return sum

    def call_back_func(res): =====> 通过回调函数拿值
    print(res.result())

    t = ThreadPoolExecutor(20)
    for i in range(1000):
    t.submit(func,i).add_done_callback(call_back_func)
    t.shutdown()

    ④线程池的回调函数

    from threading import current_thread        =====>通过这个模块拿到线程的id
    from concurrent.futures import ThreadPoolExecutor
    import time

    def func(i):
    sum = 0
    sum += i
    time.sleep(1)
    print("这是在子线程中",current_thread())
    return sum
    def call_back(sum):
    time.sleep(1)
    print("这是在回调函数中",sum.result(),current_thread())

    if __name__ == '__main__':
    t = ThreadPoolExecutor(5)
    for i in range(10):
    t.submit(func,i).add_done_callback(call_back)
    t.shutdown()
    print("这是在主线程中",current_thread())

    关于回调函数

      不管是Pool进程池的方式,还是ProcessPoolExecutor的方式开启进程池,回调函数都是由父进程调用的,和父进程没有关系

      线程池中的回调函数是子线程调用的,和父线程没有关系

      父线程不一定是主线程       主线程不一定是子线程的父线程

  • 相关阅读:
    C# Workbook读取Execl数据
    C# Task
    Json/XML序列化和反序列化
    C# RSA加解密和MD5加密
    SqlServer基本操作
    SQL Server基础优化
    Http请求基本方法
    ASP.NET MVC基础知识
    简单的五险一金计算器
    PHP基础入门(三)
  • 原文地址:https://www.cnblogs.com/lhy979/p/9525398.html
Copyright © 2020-2023  润新知