并发函数--线程

自古美人都是妖i 提交于 2020-02-17 14:47:29

计算机的执行单位以线程为单位.计算机的最小可执行单位是线程.

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

线程不可以自己独立拥有资源,线程的执行必须依赖于所属进程的资源.

线程被称作轻量级的进程.线程的切换速度不进程块.

进程中必须至少要有一个线程

GIL:全局解释锁,用来锁解释器(只有CPython解释器才有),限制只能有一个线程来访问CPU.对于线程来说,因为有了GIL所以只有并发没有并行,CPU允许一个线程执行5毫秒

线程又分为:

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

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

线程的模块:threading

线程是由:代码块,数据段,TCB(线程控制块)组成.

实现线程的两种方法:

1.直接实现

例:

from threading import Threaddef func(i):    print(i+1)for i in range(100):    t = Thread(target = func,args = (i,))  #开启100个线程    t.start()

2.继承Thread类

例:

from threading import Threadclass Mythread(Thread):   #继承Thread类来开启线程    def __init__(self):        super(Mythread, self).__init__()    def run(self):        print(1)t = Mythread()t.start()

进程和线程的区别:

1.CPU的切换进程要比CPU切换线程慢很多 所有在python中,如果I/O操作密集时,最好使用线程;计算密集情况下,最好使用进程

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

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

例: 

from threading import Thread,Lockimport timedef func():    global num    l.acquire()    tem = num    time.sleep(0.01)    num = tem - 1    l.release()num = 100lis = []l = Lock()for i in range(100):    t = Thread(target = func, )    t.start()    lis.append(t)[i.join() for i in lis]print(num)

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

5.守护线程是根据主线程执行结束才结束;守护进程是根据主进程代码执行结束而结束

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

守护线程会等待主线程结束而结束.一般把不重要的事情设置为守护线程

线程受被强制放弃CPU的原因:1.线程会受到时间片影响

               2.GIL会限制每个线程的执行时间一般都是5毫秒左右

               3.限制执行固定的bytecode(字节码)

线程的使用方法:

1.锁

  Lock是互斥锁,一把钥匙配一把锁

  Rlock是递归锁,是一个无止尽的锁,但是所有锁都有一个公用的钥匙

        在同一个线程内,递归锁可以无止尽的acquire,但互斥锁不行

        在不同的线程内,递归锁是保证只能被一个线程拿到钥匙.其他线程等待

  GIL是全局解释器锁,锁的是线程,是解释器上的锁,锁的是线程.意思是在同一时间只允许一个线程访问CPU

例:

from threading import Thread,RLock,Lockdef func1():    r.acquire()    print(456)    r.acquire()    print(123)    r.release()    r.release()def func2():    r.acquire()    print("ABC")    r.acquire()    print("abc")    r.release()    r.release()r = RLock()   #实例化一个万能锁t1 = Thread(target = func1)t2 = Thread(target = func2)t1.start()t2.start()

2.信号量

例:

from threading import Semaphore,Threadimport time,randomdef func(sem,i):    sem.acquire()    print("\033[32m 第%s个人进入 \033[0m" % i)    time.sleep(random.randint(1,3))    print("\033[35m 第%s个人出去了 \033[31m" % i)    sem.release()sem = Semaphore(5)for i in range(20):    t = Thread(target = func,args = (sem,i))    t.start()

3.事件

例:

from threading import Thread,Eventimport timedef Traffic_lights(e,):    while 1:        if e.is_set():            time.sleep(5)            print("\033[35m 红灯亮了 \033[0m")            e.clear()        else:            time.sleep(5)            print("\033[36m 绿灯亮了 \033[0m")            e.set()def car(e,i):    e.wait()    print("第%s辆车过去了" % i)e = Event()t_l = Thread(target = Traffic_lights,args = (e,))t_l.start()for i in range(20):    c = Thread(target = car,args = (e,i+1))    c.start()

4.条件

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

Condition的四个方法:acquire,release,wait,notify

wait是指让线程阻塞住

notify(n)是给wait发一个信号,让wait变不阻塞,n是指要给多少wait发信号

例:

from threading import Condition,Threaddef func(con,i):    con.acquire()         #主线程和100个子线程都在抢递归锁的一把钥匙    con.wait()     #阻塞    con.release()    print("第%s个线程开始执行了" % i)con = Condition()for i in range(10):    t = Thread(target = func,args = (con,i))    t.start()while 1:    num = int(input(">>>"))    con.acquire()    con.notify(num)    #发送一个标识让线程不阻塞num次    con.release()结果:>>>5>>>第0个线程开始执行了第1个线程开始执行了第3个线程开始执行了第4个线程开始执行了第2个线程开始执行了4>>>第7个线程开始执行了第6个线程开始执行了第5个线程开始执行了第8个线程开始执行了2>>>第9个线程开始执行了注意:如果主线程执行顺序是拿到钥匙->input->notify发送信号->换钥匙.如果主线程执行特别快,接下来极有可能主线程又拿到钥匙.那么此时即使有十个子线程的wait接收到信号,也拿不到钥匙执行不了程序.

5.定时器

语法:Timer(time,func).star()

time:睡眠时间

func:要执行的函数名

例:

from threading import Timerdef func():    print("hello world")Timer(0.1, func).start()

线程的队列:

同一进程(多线程)的队列:不能做多进程的通信

例:先进先出队列

import queueq = queue.Queue()q.put("1")q.put("2")q.put("3")print(q.get())print(q.get())结果:12

例:后进先出队列

import queueq = queue.LifoQueue()q.put(1)q.put(2)q.put(3)print(q.get())print(q.get())结果:32

例:优先级队列

import queueq = queue.PriorityQueue()q.put((1,"a"))q.put((0,"b"))q.put((9,"c"))print(q.get())print(q.get())结果:(0, 'b')(1, 'a')

例:

import queueq = queue.Queue(3)q.put(1)q.put(2)q.put(3)# q.put_nowait("a")while 1:    try:        print(q.get_nowait())    except:        print("队列空了")        breakfor i in range(0):    print(i)

优先级队列的put()接收的是元组(,):第一个元素是优先级,第二个位置是存入队列的数据

首先保证整个队列中,所有优先级的类型一致

优先级是数字(int)类型的直接比较大小,数字越小优先级越高

优先级是字符串类型的按照ASCII码比较的,比较字符串的第一个位置的ASCII,如果字符串的ASCII码相同会按照先进先出原则

 

线程池:并发

定义:在一个池子里放固定数量的线程,这些线程处于等待任务状态,一旦有任务来,就有线程自动执行

concurrent.futures这个模块是异步的调用机制

导入进程池和线程池:from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor  

==> 提交任务都用submit,多个任务用for循环+submit   shutdown()==pool进程池中的close+join

例:线程,进程和Pool进程异步的效率对比

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutorimport timefrom multiprocessing import Pooldef func(num):    sum = 0    for i in range(num):        sum += i**2    # print(sum)if __name__ == '__main__':    t_start = time.time()    t = ThreadPoolExecutor(20)    for i in range(10000):        t.submit(func,i)    t.shutdown()    t_time = time.time() - t_start    p_start = time.time()    p = ProcessPoolExecutor(5)    for i in range(10000):        p.submit(func,i)    p.shutdown()    p_time = time.time() - p_start    po_start = time.time()    po = Pool(5)    for i in range(10000):        po.apply_async(func,i)    po.close()    po.join()    po_time = time.time() - po_start    print("进程执行的时间:%s,Pool进程中异步执行的时间:%s,线程执行的时间:%s" % (p_time,po_time,t_time))

针对计算密集时:

不管是Pool进程池的异步还是ProcessPoolExecutor的进程池执行效率相当

ThreadPoolExecutor的效率差很多,所以计算密集时使用进程池

shutdown等效于Pool进程池中异步处理进程中的close+join 是指不允许向线程池中添加任务,然后让父进程(线程)等待池中所以进程(线程)执行任务

提交多个任务:1.使用for循环+submit

      2.使用map

例:for循环+submit方法拿到返回结果用result

from concurrent.futures import ThreadPoolExecutordef func(i):    return i+1t = ThreadPoolExecutor(20)lis = []for i in range(1000):    res = t.submit(func,i)    lis.append(res)t.shutdown()[print(i.result()) for i in lis]

例:map方法拿到返回结果用__next__()   返回值是一个生成器可以直接用for循环

from concurrent.futures import ThreadPoolExecutordef func(i):    return i+1t = ThreadPoolExecutor(20)obj = t.map(func,[i for i in range(1000)])t.shutdown()while 1 :    try :        print(obj.__next__())    except StopIteration:        break

回调函数:

例:

from concurrent.futures import ThreadPoolExecutordef func(num):    sum = 0    for i in range(num):        sum += i**2    return sumdef call_back(ret):    print(ret.result())t = ThreadPoolExecutor(20)for i in range(1000):    t.submit(func,i).add_done_callback(call_back)t.shutdown()

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!