并发编程 线程
线程
在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程
线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程
车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线
流水线的工作需要电源,电源就相当于cpu
所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。
多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。
例如,北京地铁与上海地铁是不同的进程,而北京地铁里的13号线是一个线程,北京地铁所有的线路共享北京地铁所有的资源,比如所有的乘客可以被所有线路拉。
开启线程的两种方式
from threading import Thread import time,os # 第一种方式 def task(): print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.getpid()) if __name__ == '__main__': t = Thread(target=task,) t.start() print('主') ''' 1.一个子进程内不开进程也不开子线程:主线程结束,该进程就结束 2.当一个进程内开启子进程时: 主线程结束,主进程要等,等所有子进程运行完毕给儿子收尸 3.当一个进程内开启多个线程时: 主线程结束并不意味着进程结束, 进程的结束指的是该进程内所有的线程都运行完毕,才应该回收进程 ''' # 第二种方式 class Mythread(Thread): def __init__(self): super().__init__() def run(self): print('%s is running' % os.getpid()) time.sleep(2) print('%s is done' % os.getpid()) if __name__ == '__main__': t = Mythread() t.start() print('主')
多线程指的是,在一个进程中开启多个线程,简单的讲:如果多个任务共用一块地址空间,那么必须在一个进程内开启多个线程。详细的讲分为4点:
1. 多线程共享一个进程的地址空间
2. 线程比进程更轻量级,线程比进程更容易创建可撤销,在许多操作系统中,创建一个线程比创建一个进程要快10-100倍,在有大量线程需要动态和快速修改时,这一特性很有用
3. 若多个线程都是cpu密集型的,那么并不能获得性能上的增强,但是如果存在大量的计算和大量的I/O处理,拥有多个线程允许这些活动彼此重叠运行,从而会加快程序执行的速度。
4. 在多cpu系统中,为了最大限度的利用多核,可以开启多个线程,比开进程开销要小的多。(这一条并不适用于python)
线程对象的其它属性和方法
from threading import Thread,current_thread,enumerate,active_count import time,os def task(): print('%s is running' %current_thread().getName()) # 线程名 current_thread()当前线程 time.sleep(2) print('%s is done' %os.getpid()) if __name__ == '__main__': # t = Thread(target=task,name='xxx') t = Thread(target=task) t.start() # t.join() # print(t.name) # 线程名 Thread-1 print(enumerate()) # 当前或者的线程对象 print(active_count()) # 当前活着线程的线程数 print('主',current_thread().getName()) # MainThread
线程与进程内存空间占用
# 进程之间内存空间隔离 from multiprocessing import Process n = 100 def task(): global n n = 0 if __name__ == '__main__': t = Process(target=task,) t.start() t.join() print('主',n) # 100 # 线程之间内存空间共享 from threading import Thread n = 100 def task(): global n n = 0 if __name__ == '__main__': t = Thread(target=task,) t.start() t.join() print('主',n) # 0
线程池
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor from threading import current_thread import time,random,os def task(n): print('%s is running'%current_thread().getName()) time.sleep(random.randint(1,3)) return n**2 if __name__ == '__main__': # t = ProcessPoolExecutor() # 默认是cpu的核数 # print(os.cpu_count()) # 查看cpu核数 t = ThreadPoolExecutor(3) # 默认为cpu的核数*5 objs = [] for i in range(10): obj = t.submit(task,i) objs.append(obj) t.shutdown(wait=True) for obj in objs: print(obj.result()) print('主',current_thread().getName())
异步调用和回调函数
import requests from threading import Thread,current_thread from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor import time def get(url): print('%s GET %s'%(current_thread().getName(),url)) response = requests.get(url) if response.status_code == 200: return {'url':url,'text':response.text} # print(type(response.text)) # <class 'str'> def parse(obj): res = obj.result() print('[%s] <%s> (%s)' % (current_thread().getName(), res['url'],len(res['text']))) # print('[%s] parse res [%s]'%(res['url'],len(res['text']))) if __name__ == '__main__': urls = [ 'https://www.python.org', 'https://www.baidu.com', 'https://www.jd.com' ] t = ThreadPoolExecutor(2) # t = ProcessPoolExecutor(2) for url in urls: t.submit(get,url).add_done_callback(parse) # parse(obj) t.shutdown(wait=True) print('主') ''' 异步调用: 提交完任务(为该任务绑定一个回调函数),不用在原地等任务执行完毕拿到结果,可以直接提交下一个任务 一个任务一旦执行完毕就会自动触发回调函数的运行 回调函数的参数是单一的: 回调函数的参数就是它所绑定任务的返回值 ''' # 进程池,回调的活由主进程干 # 线程池,回调的活都有可能干