赞
踩
线程:
线程(Thread)也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行
并发与并行:
单核CUP执行多任务,由于CUP执行代码都是顺序执行的,当执行多个任务的时候操作系统会轮流让各个任务交替执行,由于执行速度过快,感觉是所有的任务同时执行一样,称为并发,当任务数小于CUP数量时,这是任务是真正的在一起运行,称为并行。
同步与异步:
同步(协同步调):线程在访问某 一资源时,获得了资源的返回结果后才会执行其他操作。
异步(步调各异):与同步相对,在访问某一资源时,无论资源是否有返回结果,都会进行下一步操作,当资源有结果时,系统会自动通知线程。
举例:创建两个普通函数
import time def fun(): for i in range(3): time.sleep(1) print('fun:', i) def fun1(): for i in range(3): time.sleep(1) print('fun1:', i) st = time.time() fun() fun1() et = time.time() print("运行时间:", et - st) 运行结果: fun: 0 fun: 1 fun: 2 fun1: 0 fun1: 1 fun1: 2 运行时间: 6.063438653945923
Threading模块
通过多线程对多任务的处理,通过threading中的Thread模块。如下通过Thread创建一个线程对象,将需要运行的函数名t赋值给target,调用对象的start方法来启动线程。
import time from threading import Thread def fun(): for i in range(3): time.sleep(1) print('fun:', i) def fun1(): for i in range(3): time.sleep(1) print('fun1:', i) st = time.time() fun = Thread(target=fun) # 创建线程对象 fun.start() # 启动线程 fun1() et = time.time() print("运行时间:", et - st) 结果: fun: 0 fun1: 0 fun1: 1 fun: 1 fun: 2 fun1: 2 运行时间: 3.0180318355560303
如果fun1也通过创建线程的方式来启动,来看下效果:
import time from threading import Thread def fun(): for i in range(3): time.sleep(1) print('fun:', i) def fun1(): for i in range(3): time.sleep(1) print('fun1:', i) st = time.time() fun = Thread(target=fun) # 创建线程对象 fun1 = Thread(target=fun1) # 创建线程对象 fun.start() # 启动线程 fun1.start() # 启动线程 et = time.time() print("运行时间:", et - st) # 主线程 结果: 运行时间: 0.0009970664978027344 fun: 0 fun: 1 fun: 2 fun1: 0 fun1: 1 fun1: 2
这时候主线程并print并没有等待子线程运行结束,现有需求需要等待子线程执行结束后才运行主线程,可以用join()方法来控制等待子线程运行的时间,默认是等待子线程运行结束。
举例:
import time from threading import Thread def fun(): for i in range(3): time.sleep(1) print('fun:', i) def fun1(): for i in range(3): time.sleep(1) print('fun1:', i) st = time.time() fun = Thread(target=fun) # 创建线程对象 fun1 = Thread(target=fun1) # 创建线程对象 fun.start() # 启动线程 fun1.start() # 启动线程 fun.join() # 等待fun线程运行结束,fun.join(2) 等待2秒 et = time.time() print("运行时间:", et-st) 结果: fun: 0 fun1: 0 fun1: 1 fun: 1 fun: 2 fun1: 2 运行时间: 3.0180318355560303
任务函数有参数时
方式一:元组的形式赋值给args
import time from threading import Thread def fun(name,age): for i in range(3): time.sleep(1) print(f'fun:{name},{age}', i) fun = Thread(target=fun, args=('MING',19)) # 创建线程对象,函数名赋值给args fun.start() 结果: fun:MING,19 0 fun:MING,19 1 fun:MING,19 2
方式二:字典的方式赋值给kwargs
import time from threading import Thread def fun(name,age): for i in range(3): time.sleep(1) print(f'fun:{name},{age}', i) fun = Thread(target=fun, kwargs={'name': 'MING', 'age': 19}) # 创建线程对象 fun.start() 结果: fun:MING,19 0 fun:MING,19 1 fun:MING,19 2
自定义线程类
重写线程类的run方法,在run方法中写线程执行的具体代码,调用start方法时会默认执行run下面的代码。
import time from threading import Thread class MyThread(Thread): def __init__(self,name): super().__init__() # 重写init方法 self.name = name def run(self): for i in range(10): time.sleep(1) print(f'{self.name}请求第{i}次') if __name__ == '__main__': for i in range(5): t = MyThread("MING") t.start()
多线程共享全局变量
from threading import Thread a = 0 def fun(): global a for i in range(100): a += 1 print("fun:", a) def fun1(): global a for i in range(100): a += 1 print("fun1:", a) if __name__ == '__main__': Thread(target=fun).start() Thread(target=fun1).start() >>> fun: 100 >>> fun1: 200
多线程是无法利用多核CPU的资源,在同一时间,只能有一个线程在执行,因为python解释器有把GIL(Global Interpreter Lock)全局解释器锁。
如图:当线程想要执行,必须先拿到GIL,可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。GIL只在cpython中才有,因为cpython调用的是c语言的原生线程,所以他不能直接操作cpu,只能利用GIL保证同一时间只能有一个线程拿到数据。
而当共享全局变量的时候则会出现新的问题,python线程执行的切换机制:
1.线程执行遇到IO耗时操作,比如sleep,文件的输入输出,网络请求等。
2.线程执行时间达到一个阈值后
当启动多个线程同时,引用的全局变量会造成资源竞争而导致数据不准确
如下:
from threading import Thread a = 0 def fun(): global a for i in range(100000): a += 1 print("fun:", a) def fun1(): global a for i in range(100000): a += 1 print("fun1:", a) if __name__ == '__main__': Thread(target=fun).start() Thread(target=fun1).start() print('a:',a) >>> fun:100000 >>> fun1:200000 >>> a: 165424
互斥锁:Lock类
而解决上面的问题可以通过引入互斥锁,状态为锁定/非锁定,当某个线程更改共享数据时,先将其锁定,此时资源的状态为 “锁定” ,其他线程不能更改直到该线程释放资源,将资源的状态变为 ”非锁定“ , 其他线程才能再次锁定该资源。
l = threading.Lock() #创建锁
l.acquire() # 锁定
l.release() # 释放
举例:
from threading import Thread, Lock a = 0 lock = Lock() # 创建锁 def fun(): global a for i in range(100000): lock.acquire() # 上锁 a += 1 lock.release() # 释放锁 print("fun:", a) def fun1(): global a for i in range(100000): lock.acquire() # 上锁 a += 1 lock.release() # 释放锁 print("fun1:", a) if __name__ == '__main__': t = Thread(target=fun) t.start() t1 = Thread(target=fun1) t1.start() t.join() t1.join() print('a:',a) >>> fun:154229 >>> fun1:165424 >>> a: 200000
线程池:ThreadPoolExecutor
通过ThreadPoolExecutor 创建线程池,submit提交任务到线程池。max_workers指定线程池中的数量
import time from concurrent.futures import ThreadPoolExecutor def work(name): for i in range(5): time.sleep(1) print(f"{name}----{i}----") with ThreadPoolExecutor(max_workers=4) as tp: # 创建线程池 tp.submit(work, 'MING') # 往线程提交任务 tp.submit(work, 'HOUX') tp.shutdown() # 等待线程池中所有的任务执行完成 print("---主线程---") 运行结果: MING----0----HOUX----0---- MING----1----HOUX----1---- HOUX----2----MING----2---- MING----3----HOUX----3---- HOUX----4----MING----4---- ---主线程---
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。