线程

1、什么是线程

  1. 协程,又称微线程,纤程。英文名Coroutine
  2. 线程是操作系统的最小单位
  3. 线程是进程真正的执行者,是一些指令的集合(进程资源的拥有者)
  4. 同一个进程下的多个线程共享内存空间,数据直接访问(数据共享)
  5. 为了保证数据安全,必须使用线程锁

说明:下面利用for循环同时启动50个线程并行执行,执行时间是3秒而不是所有线程执行时间的总和

import threading
import time

def sayhi(num): #定义每个线程要运行的函数
    print("running on number:%s" %num)
    time.sleep(3)
for i in range(50):
    t = threading.Thread(target=sayhi,args=('t-%s'%i,))
    t.start()

协程的优点

(1)无需线程上下文切换的开销,协程避免了无意义的调度,由此可以提高性能(但也因此,程序员必须自己承担调度的责任,
同时,协程也失去了标准线程使用多CPU的能力)
(2)无需原子操作锁定及同步的开销
(3)方便切换控制流,简化编程模型
(4)高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

协程的缺点

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

2、GIL锁和线程锁

  • GIL全局解释器锁
    • 在Python全局解释器下,保证同一时间只有一个线程运行
    • 防止多个线程都修改数据
  • 程锁(互斥锁)
    • GIL锁只能保证同一时间只能有一个线程对某个资源操作,但当上一个线程还未执行完毕时可能就会释放GIL,其他线程就可以操作了
    • 线程锁本质把线程中的数据加了一把互斥锁
        • 加上线程锁之后所有其他线程,读都不能读这个数据
    • 有了GIL全局解释器锁为什么还需要线程
      • 因为cpu是分时使用的
  • 再有GIL的情况下执行count = count + 1会出错原因解释,用线程锁解决方法
# 1)第一步:count = 0   count初始值为0
# 2)第二步:线程1要执行对count加1的操作首先申请GIL全局解释器锁
# 3)第三步:调用操作系统原生线程在操作系统中执行
# 4)第四步:count加1还未执行完毕,时间到了被要求释放GIL
# 5)第五步:线程1释放了GIL后线程2此时也要对count进行操作,此时线程1还未执行完,所以count还是0
# 6)第六步:线程2此时拿到count = 0后也要对count进行加1操作,假如线程2执行很快,一次就完成了
#    count加1的操作,那么count此时就从0变成了1
# 7)第七步:线程2执行完加1后就赋值count=1并释放GIL
# 8)第八步:线程2执行完后cpu又交给了线程1,线程1根据上下文继续执行count加1操作,先拿到GIL
#    锁,完成加1操作,由于线程1先拿到的数据count=0,执行完加1后结果还是1
# 9)第九步:线程1将count=1在次赋值给count并释放GIL锁,此时连个线程都对数据加1,但是值最终是1

  • 死锁的定义
    • 两个以上的线程或进程在执行过程中,因争夺资源而造成的一种相互等待的现象,若无外力作用,它们都将无法推进下去

3、多线程或线程池

  1. 线程有哪些模块
    多线程模块

  2. 线程池有哪些模块?
    线程池模块

4、join()和setDaemon()

  • join()

实现所有线程都执行结束后再执行主线程

import threading
import time
start_time = time.time()

def sayhi(num): #定义每个线程要运行的函数
    print("running on number:%s" %num)
    time.sleep(3)
for i in range(50):
    t = threading.Thread(target=sayhi,args=('t-%s'%i,))
    t.setDaemon(True)  #把当前线程变成守护线程,必须在t.start()前设置
    t.start()          #启动一个线程,程序不会阻塞
print('cost time:',time.time() - start_time)

  • setDaemon()

守护线程,主线程退出时,需要子线程随主线程退出

import threading
import time
start_time = time.time()

def sayhi(num): #定义每个线程要运行的函数
    print("running on number:%s" %num)
    time.sleep(3)
for i in range(50):
    t = threading.Thread(target=sayhi,args=('t-%s'%i,))
    t.setDaemon(True)  #把当前线程变成守护线程,必须在t.start()前设置
    t.start()          #启动一个线程,程序不会阻塞
print('cost time:',time.time() - start_time)

5、Python中使用过的线程模块?

  • 5.1 threading
    1. Python提供了几个用于多线程编程的模块,包括thread、threading和Queue等。
    2. thread和threading模块允许程序员创建和管理线程。
import threading
import time

def sayhi(num): #定义每个线程要运行的函数
    print("running on number:%s" %num)
    time.sleep(3)
    
for i in range(50):
    t = threading.Thread(target=sayhi,args=('t-%s'%i,))
    t.start()
  • 5.2 concurrent.futures
    1、简介 参考官网

      1、Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码
      2、但是当项目达到一定的规模,频繁创建/销毁进程或者线程是非常消耗资源的,这个时候我们就要编写自己的线程池/进程池,以空间换时间。
      3、但从Python3.2开始,标准库为我们提供了concurrent.futures模块,它提供了ThreadPoolExecutor和ProcessPoolExecutor两个类,
      4、实现了对threading和multiprocessing的进一步抽象,对编写线程池/进程池提供了直接的支持。
      2、Executor和Future
    
  1. Executor

    1. concurrent.futures模块的基础是Exectuor,Executor是一个抽象类,它不能被直接使用。

    2. 但是它提供的两个子类ThreadPoolExecutor和ProcessPoolExecutor却是非常有用

    3. 我们可以将相应的tasks直接放入线程池/进程池,不需要维护Queue来操心死锁的问题,线程池/进程池会自动帮我们调度。

  2. Future

    1. Future你可以把它理解为一个在未来完成的操作,这是异步编程的基础,
    2. 传统编程模式下比如我们操作queue.get的时候,在等待返回结果之前会产生阻塞,cpu不能让出来做其他事情
    3. 而Future的引入帮助我们在等待的这段时间可以完成其他的操作。
  • concurrent.futures.ThreadPoolExecutor 抓取网页


import requests
from concurrent.futures import ThreadPoolExecutor

def fetch_request(url):
    result = requests.get(url)
    print(result.text)

url_list = [
    'https://www.baidu.com',
    'https://www.google.com/',         #google页面会卡住,知道页面超时后这个进程才结束
    'http://dig.chouti.com/',          #chouti页面内容会直接返回,不会等待Google页面的返回
]

pool = ThreadPoolExecutor(10)            # 创建一个线程池,最多开10个线程
for url in url_list:
    pool.submit(fetch_request,url)       # 去线程池中获取一个线程,线程去执行fetch_request方法

pool.shutdown(True)  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值