python——多线程,多进程,协程

线程,进程

定义:python

进程: 是对各类资源管理的集合,qq 要以一个总体的形式暴露给操做系统管理,里面包含对各类资源的调用,内存的管理,网络接口的调用等算法

线程: 是操做系统最小的调度单位, 是一串指令的集合。编程

进程要想操做CPU,就必需要建立一个线程(进程中至少包含一个线程)网络

区别:多线程

1.线程共享内存空间(共享数据等),进程的内存空间是独立的并发

2.同一进程的线程之间能够相互交流 ,2个进程之间的交流必须经过一个中间代理app

3.线程能够操做和控制其余线程(同一进程下),进程只能操做和控制子进程。框架

对主线程的更改可能会影响到其余线程的工做,对父进程的更改(除非关闭)不会影响子进程。(子进程还能够派生子进程)异步

Python中的多线程 

import threading

def run(n):
  print('运行线程',n)

for i in range(10):     # 建立10个线程
    t = threading.Thread(target=run, args=(i,))    # 线程运行的函数和参数
    t.setDaemon(True)   # 设置为守护线程(在主线程线程结束后自动退出,默认为False即主线程线程结束后子线程仍在执行)
    t.start()   # 启动线程

上述代码建立了10个“前台”线程,而后控制器就交给了CPU,CPU根据指定算法进行调度,分片执行指令。scrapy

更多方法:

  • start            线程准备就绪,等待CPU调度
  • setName      为线程设置名称
  • getName      获取线程名称
  • setDaemon   设置为后台线程或前台线程(默认)
                         若是是后台线程,主线程执行过程当中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均中止
                         若是是前台线程,主线程执行过程当中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序中止
  • join              逐个执行每一个线程,执行完毕后继续往下执行,该方法使得多线程变得无心义
  • run              线程被cpu调度后自动执行线程对象的run方法

互斥锁(Lock、RLock)

因为线程之间是进行随机调度,而且每一个线程可能只执行n条执行以后,当多个线程同时修改同一条数据时可能会出现脏数据,因此,出现了线程锁 - 同一时刻容许一个线程执行操做。

import threading
import time

gl_num = 0

lock = threading.RLock()    # 定义线程锁


def Func():
    lock.acquire()      # 开始锁
    global gl_num
    gl_num += 1
    time.sleep(1)
    print(gl_num)
    lock.release()      # 结束锁


for i in range(10):
    t = threading.Thread(target=Func)
    t.start()
    
"""
没有锁的时候打印:
>> 10,10,10,10,10,10,10,10,10,10

有锁的时候打印:
>> 1,2,3,4,5,6,7,8,9,10
"""

信号量(Semaphore)

互斥锁同时只容许一个线程更改数据,而信号量锁是同时容许必定数量的线程更改数据 ,多个线程同时执行完毕。

import threading, time


def run(n):
    semaphore.acquire()     # 信号量锁开始
    time.sleep(1)
    print("当前运行线程为: %s" % n)
    semaphore.release()     # 结束


if __name__ == '__main__':

    num = 0
    semaphore = threading.BoundedSemaphore(5)    # 最多容许5个线程同时运行(5个5个一块儿出来)
    for i in range(20):
        t = threading.Thread(target=run, args=(i,))
        t.start()

事件(event)

python线程的事件用于主线程控制其余线程的执行,事件主要提供了两个方法:event.set()设定,event.clear()没设定。

  • event.wait():等待设定
  • event.is_set():判断是否设定
 1 import time,threading
 2 
 3 event = threading.Event()
 4 
 5 def lighter():
 6     count=0
 7     event.set()     #设定
 8     while True:
 9         if count<10 and count>=5:
10             event.clear()   #清除设定
11             print("\033[41;1m红灯\033[0m")
12             time.sleep(1)
13         elif count>10:
14             count=0
15             event.set()
16         else:
17             print("\033[42;1m绿灯\033[0m")
18             time.sleep(1)
19         count+=1
20 
21 def car(name):
22     while True:
23         if event.is_set():  #判断是否设定
24             print("\033[32;1m[%s] run...\033[0m"%name)
25             time.sleep(1)
26         else:
27             print('\033[31;1m [%s] stop...'%name)
28             event.wait()    #等待设定
29             print('\033[33;1m [%s]走咯'%name)
30 
31 
32 light=threading.Thread(target=lighter,)
33 
34 light.start()
35 
36 car1=threading.Thread(target=car,args=('Tesla',))
37 
38 car1.start()
红绿灯

Python中的多进程 

多进程特色:

  • 每个进程都是由父进程启动的
  • 子进程被父进程启动后就是独立的(父进程copy了一份给子进程)
from multiprocessing import Process

def foo(i):
    print('say hi', i)

if __name__ == '__main__':
    for i in range(10):
        p = Process(target=foo, args=(i,))
        p.start()

获取进程id:

 1 from multiprocessing import Process     #多进程
 2 import os
 3 
 4 def info(title):
 5     print(title)
 6     print('module name:', __name__)
 7     print('parent process:', os.getppid())  #获取父进程的端口号
 8     print('process id:', os.getpid())   #获取当前进程的端口号
 9     print('\n')
10 
11 def f(name):
12     info('\033[31;1mcalled from child process function f\033[0m')
13     print('hello', name)
14 
15 if __name__ == '__main__':
16     info('\033[32;1mmain process line\033[0m')
17     p = Process(target=f, args=('bob',))
18     p.start()
19     p.join()
20 
21 get进程id
View Code

进程数据共享

  • 进程各自持有一份数据,默认没法共享数据
  •  当建立进程时(非使用时),共享数据会被拿到子进程中,当进程中执行完毕后,再赋值给原值。

经过队列共享数据:

from multiprocessing import Process, Queue

def run(qq):
    qq.put("123")


if __name__=='__main__':
    q=Queue()    #生成一个队列,经过队列进行传递数据
    p=Process(target=run,args=(q,))
    p.start()
    print(q.get())
    p.join()

经过字典共享数据:

from multiprocessing import Process, Manager
import os
def f(d, l):
    d[os.getpid()] =os.getpid()
    l.append(os.getpid())
    print(l)

if __name__ == '__main__':
    with Manager() as manager:  #Manager()=manager
        d = manager.dict() #{} #生成一个字典,可在多个进程间共享和传递

        l = manager.list(range(5))#生成一个列表,可在多个进程间共享和传递
        p_list = []
        for i in range(10):
            p = Process(target=f, args=(d, l))
            p.start()
            p_list.append(p)
        for res in p_list: #等待结果
            res.join()

        print(d)
        print(l)

进程锁:

在多个进程共享一个屏幕时可能会致使输出的数据变乱。

from multiprocessing import Process, Lock

def f(l, i):
    l.acquire()     #设置进程锁
    try:
        print('hello world', i)
    finally:
        l.release()     #取消进程锁

if __name__ == '__main__':
    l = Lock()  #实例化锁
    for num in range(10):
        Process(target=f, args=(l, num)).start()

进程池:

进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,若是进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。

进程池中有两个方法:

  • apply:串行
  • apply_async:并行

 1 from  multiprocessing import Pool
 2 import time
 3 import os
 4 
 5 def Foo(i):
 6     time.sleep(2)
 7     print("in process",os.getpid())
 8     return i + 100
 9 
10 def Bar(arg):
11     print('-->exec done:', arg,os.getpid())
12 
13 if __name__ == '__main__':
14     #freeze_support()
15     pool = Pool(processes=3) #容许进程池同时放入5个进程
16     print("主进程",os.getpid())
17     for i in range(10):
18         pool.apply_async(func=Foo, args=(i,), callback=Bar) #callback=回调
19         #pool.apply(func=Foo, args=(i,)) #串行
20         #pool.apply_async(func=Foo, args=(i,)) #并行
21     print('end')
22     pool.close()
23     pool.join() #进程池中进程执行完毕后再关闭,若是注释,那么程序直接关闭。.join()
View Code

协程(微线程)

经过单线程实现并发(协程只有一个线程,so不用锁)

协程的好处:

  • 无需线程上下文切换的开销
  • 无需原子操做锁定及同步的开销
  • "原子操做(atomic operation)是不须要synchronized",所谓原子操做是指不会被线程调度机制打断的操做;这种操做一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另外一个线程)。原子操做能够是一个步骤,也能够是多个操做步骤,可是其顺序是不能够被打乱,或者切割掉只执行部分。视做总体是原子性的核心。
  • 方便切换控制流,简化编程模型
  • 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。因此很适合用于高并发处理。

缺点:

  • 没法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程须要和进程配合才能运行在多CPU上.固然咱们平常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
  • 进行阻塞(Blocking)操做(如IO时)会阻塞掉整个程序

使用yield实现协程操做例子

 1 import time
 2 
 3 def consumer(name):
 4     print("--->starting eating baozi...")
 5     while True:
 6         new_baozi = yield
 7         print("[%s] is eating baozi %s" % (name, new_baozi))
 8         # time.sleep(1)
 9 
10 def producer():
11     r = con.__next__()
12     r = con2.__next__()
13     n = 0
14     while n < 5:
15         n += 1
16         con.send(n)
17         con2.send(n)
18         time.sleep(1)
19         print("\033[32;1m[producer]\033[0m is making baozi %s" % n)
20 
21 if __name__ == '__main__':
22     con = consumer("c1")
23     con2 = consumer("c2")
24     p = producer()
25 
26 yield
View Code

同步与异步性能差异

 1 import gevent
 2 
 3 def task(pid):
 4     gevent.sleep(0.5)
 5     print('Task %s done' % pid)
 6 
 7 def synchronous():  #每一个都要等0.5s,须要5s
 8     for i in range(1, 10):
 9         task(i)
10 
11 def asynchronous():  #一共等0.5s
12     threads = [gevent.spawn(task, i) for i in range(10)]
13     gevent.joinall(threads)
14 
15 print('Synchronous:')
16 synchronous()
17 
18 print('Asynchronous:')
19 asynchronous()
20 
21 同步与异步
View Code

总结:

一、多进程,多线程,协程

操做系统方面的多进程,多线程,协程:

操做系统能够开多个进程,一个进程能够有多个线程,多个线程能够被分配到不一样的核心上跑,但实际上每一个核心上只有一个线程,只是这个线程在不停的进行上下文的切换,给咱们一种并发的感受。

协程:单线程的调度机制。它的做用是让原来要使用异步+回调方式(调用线程)写的非人类代码,能够用看似同步的方式写出来。协程是先出现的,但它有明显的时间差,没有并发的感受,因此出现了线程。

python的多进程,多线程,协程:

但python的多线程只能在一个核心上跑(创始人没想到会有多核出现),就是单核的上下文切换,因此很鸡肋。因而协程在python大展拳脚,好多框架都是使用协程来解决多任务的,而不是线程(scrapy,tornado)。

python中多进程,多线程,协程的使用:

IO密集型:多线程/协程(能够用异步),cpu占用率低,单个cpu核心就够了

CPU密集型:多进程,多给它几个核心提高性能

二、python多线程不用join,进程须要(不然子进程会在进程结束时强制被关闭)

1 python 默认参数建立线程后,无论主线程是否执行完毕,都会等待子线程执行完毕才一块儿退出,有无join结果同样

2 若是建立线程,而且设置了daemon为true,即thread.setDaemon(True), 则主线程执行完毕后自动退出,不会等待子线程的执行结果。并且随着主线程退出,子线程也消亡。

3 join方法的做用是阻塞,等待子线程结束,join方法有一个参数是timeout,即若是主线程等待timeout,子线程尚未结束,则主线程强制结束子线程。

4 若是线程daemon属性为False, 则join里的timeout参数无效。主线程会一直等待子线程结束。

5 若是线程daemon属性为True, 则join里的timeout参数是有效的, 主线程会等待timeout时间后,结束子线程。此处有一个坑,即若是同时有N个子线程join(timeout),那么实际上主线程会等待的超时时间最长为 N * timeout, 由于每一个子线程的超时开始时刻是上一个子线程超时结束的时刻。

相关文章
相关标签/搜索