python之进程,线程,和协程


线程进程介绍

  1. 工作最小单元是线程 

  2. 应用程序 -> 至少有一个进程 -> 至少有一个线程

  3. 应用场景:
    IO密集型:线程
    计算密集型:进程
  4. GIL,全局解释器锁。
    - 保证同一个进程中只有一个线程同时被调度

线程进程的区别

  1.进程 内存独立,线程共享同一进程的内存
  2.进程是资源的集合, 线程是执行单位
  3. 进程之间不能直接互相访问,线程可以互相通信
  4. 创建新进程非常消耗系统资源, 线程非常轻量,只保存线程需要运行时的必要数据,如上下文,程序堆栈
  5. 同一进程里的线程可以互相控制,父进程之可以控制子进程

CPU是如何处理线程的

  当某个程序下,所有开启的线程准备就绪,此时CPU就会在这些线程之间不停的切换处理每个线程的逻辑,之后返回结果,对于人的肉眼而言,就是并发的效果

python的多进程,多线程分析

  假如系统有两个核心,此时呢有一个应用进程,里面开启了三个线程,每个进程中有一个主线程和两个子线程,CPU在处理线程的时候会随机调用,所以单个进程中
的不同的线程,有可能会被两个核心在同一时刻处理,从而实现利用多核优势并发,但是在python中,因为GIL(全局解释器锁)的存在,同一时刻每个进程中只能有一
个线程被处理。假设此时有一个进程,这种情况下python就不能利用多核的优势,因为只允许一个线程被处理对不对。另一个核心就浪费了呀!所以对于python而言,
做IO密集型任务的时候,才适合用多线程,因为不依赖CPU,而对于计算密集型任务,就使用多进程。

   建立http请求属于IO请求

python线程

创建      

  我们来创建一个线程玩玩,python中线程模块为threading              

#_*_coding:utf-8_*_
__author__='wuzhihu'

import threading   导入线程模块

import time

def func(arg):       在这里我们首先创建一个函数来作为我们的任务
    time.sleep(1)
    print(arg)

for i in range(8):      利用for循环来同时开启多个线程
    t=threading.Thread(target=func,args=[i,])    创建一个线程
    t.start()   启动线程,告诉CPU准备就绪,来调用我把

print('end')   主线程执行一个打印任务

  然后执行看返回结果,可以看到主线程先走到末尾了,而且各个线程的完结也都不一样,这也证实了,CPU是随机处理各个线程的,线程在启动了之后就会等在那里

"C:\Program Files\Python35\python.exe" F:/oldboy/day9/线程.py
end
4
3
2
5
1
0
6
7

指定主线程是否等待子线程  

  我们有时候呢可能会需要说,主线程结束的时候所有子线程也终止,我们来看看怎么做,如下添加一行,注意:必须加到start之前

#_*_coding:utf-8_*_
__author__='wuzhihu'

import threading

import time

def func(arg):
    time.sleep(1)
    print(arg)

for i in range(8):
    t=threading.Thread(target=func,args=[i,])
    t.setDaemon(True)
    t.start()

print('end')

  执行结果

"C:\Program Files\Python35\python.exe" F:/oldboy/day9/线程.py
end

  所以呢,这里如果不指定setDaemon为True的话,他默认为False

设定下个线程执行时等待上个线程的时间

 1 #_*_coding:utf-8_*_
 2 __author__='wuzhihu'
 3 
 4 import threading
 5 
 6 import time
 7 
 8 def func(arg):
 9     time.sleep(3)
10     print(arg)
11 
12 for i in range(8):
13     t=threading.Thread(target=func,args=[i,])
14     t.setDaemon(True)
15     t.start()
16     t.join(1)   指定等待的时间,默认为一直等待,知道上个线程执行完成,在执行下个线程17 
18 print('end')

线程里面的函数是如何调用的

  可以看出来,是通过threading这个类中的run方法调用的

class Mythread(threading.Thread):
    def __init__(self,func,*args,**kwargs):
        super(Mythread,self).__init__(*args,**kwargs)
        self.func=func

    def run(self):
        self.func('hahaha')

def func(arg):
    time.sleep(1)
    print(arg)
    
obj=Mythread(func=func)
obj.start()

运行结果
"C:\Program Files\Python35\python.exe" F:/oldboy/day9/线程.py
hahaha

线程锁    

  有时候呢,我们会遇到某些操作不允许并发的情况,这时候就需要用到锁

允许一个线程进出

import threading
import time


v=10
# lock=threading.Lock()  #创建一把锁
lock=threading.RLock()  ##可以递归解锁
def func(arg):
    time.sleep(2)
    lock.acquire()  #锁住,从此处开始只允许进入一个线程
    lock.acquire()  ##双重锁
    global v
    v-=1
    print(v)
    lock.release()  #解锁
    lock.release()   #上几把锁,就解几把



for i in range(10):
    t=threading.Thread(target=func,args=[i,])
    t.start()
print('end')

运行结果:

"C:\Program Files\Python35\python.exe" F:/oldboy/day9/线程锁.py
end
9
8
7
6
5
4
3
2
1
0 

多个人同时使用锁  

  只需将上面的创建锁换成下面的方式即可,

lock=threading.BoundedSemaphore(3)   3表示允许三个线程同时进去

事件锁  

import threading
import time


lock=threading.Event()    创建事件锁

def func(arg):

    time.sleep(2)
    lock.wait()      所有线程都在这里被锁住
    print(arg)

for i in range(10):
    t = threading.Thread(target=func, args=[i, ])
    t.start()

while True:
    value=input('>>')
    if value== '1':
        lock.set()     解锁

想释放多少进程就释放多少  

import threading
import time

lock=threading.Condition()   ##创建自由锁

def func(arg):

    time.sleep(2)
    lock.acquire()
    lock.wait()
    print(arg)
    lock.release()

for i in range(10):
    t = threading.Thread(target=func, args=[i, ])
    t.start()

while True:
    value=input('>>')
    lock.acquire()
    lock.notify(int(value))   ##根据输入多少来释放相应的线程个数
    lock.release()

连接池  

  预留固定的线程等待连接

from concurrent.futures import ThreadPoolExecutor
import time
import requests

def tak(url):
    time.sleep(2)
    response=requests.get(url)
    print('返回结果',url,len(response.content))

pool=ThreadPoolExecutor(2)   建立一个线程池,预留两个线程
url_list=[
    'http://www.baidu.com',
    'http://hao123.com',
    'http://www.powercdn.com',
]

for url in url_list:
    print('开始请求',url)
    pool.submit(tak,url)   调用池子

回调函数  

from concurrent.futures  import ThreadPoolExecutor

import time
import requests

pool=ThreadPoolExecutor(3)

def download(url):
    res=requests.get(url)
    return res        返回结果

def txt(future):
    download_res = future.result()    
    print('处理中',download_res.url,download_res.status_code)


url_list=[
    'http://www.baidu.com',
    'http://hao123.com',
    'http://www.powercdn.com',
]
for url in url_list:
    print('开始请求',url)
    future=pool.submit(download,url)    拿到download处理返回结果

    future.add_done_callback(txt)   调用txt函数处理download拿回来的数据

基于线程池的分布执行方式扩展  

from concurrent.futures import ThreadPoolExecutor

import requests

def download(url):
    res=requests.get(url)
    return res

def run(url_list):
    pool=ThreadPoolExecutor(2)
    for item in url_list:
        url=item['url']
        call=item['call']
        future=pool.submit(download,url)
        future.add_done_callback(call)
import 基于线程池的分布执行方式扩展

def f1(future):
    res=future.result()
    print(res.text)
def f2(future):
    pass

url_list=[
    {'url':'http://www.baidu.com','call':f1},
    {'url':'http://hao123.com','call':f2}



]

基于线程池的分布执行方式扩展.run(url_list)

Timer 

  定时器,规定时间以后执行某个操作

#_*_conding:utf-8_*_
__author__='wuzhihu'

from  threading import Timer

def hello():
    print('hello world')

t=Timer(2,hello)  ##设定为两秒以后执行hello函数
t.start()

python进程 

创建    

from multiprocessing import Process   导入模块

import threading
import time

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

if __name__=='__main__':       window下执行进程必须这样做,不然会报错呦

    for i in range(20):
        p=Process(target=hello,args=(i,))
        p.start()

python协程  

  协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

  协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;