主要内容:

  线程的一些其他方法

  线程事件

  线程队列

  线程池

  GIL锁

  协程

  Greenlet

  Gevent

一. 线程(threading)的一些其他方法

  1. from threading import Thread
  2. import threading
  3. import time
  4.  
  5. def work():
  6. time.sleep(1)
  7. print("子线程对象>>>", threading.current_thread()) # 子线程对象
  8. print("子线程名称>>>", threading.current_thread().getName()) # 子线程名称
  9. print("子线程ID>>>", threading.get_ident()) # 子线程ID
  10.  
  11. if __name__ == '__main__':
  12. t = Thread(target=work) # 创建子线程
  13. t.start() # 开启子线程
  14. print("主线程对象>>>", threading.current_thread()) # 主线程对象
  15. print("主线程名称>>>", threading.current_thread().getName()) # 主线程名称
  16. print("主线程ID>>>", threading.current_thread().ident) # 主线程ID
  17. print("主线程ID>>>", threading.get_ident()) # 主线程ID
  18.  
  19. time.sleep(1) # 阻塞住,此时主线程代码运行的同时子线程代码也在运行
  20. print(threading.enumerate()) # 拿到所有正在运行的线程对象(包括主线程)
  21. print(threading.active_count()) # 拿到所有正在运行的线程对象的数量
  22. print("主线程/主进程执行完毕")

threading的一些其他方法

二. 线程事件

同进程的一样. 线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行.

  事件的基本方法:

  1. event.isSet():返回event的状态值;
  2. event.wait():如果 event.isSet()==False将阻塞线程;
  3. event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
  4. event.clear():恢复event的状态值为False

  举例说明:

有多个工作线程尝试链接MySQL,我们想要在链接前确保MySQL服务正常才让那些工作线程去连接MySQL服务器,如果连接不成功,都会去尝试重新连接。我们现在采用threading.Event机制来协调各个工作线程的连接操作.

  1. MySQL简述:
  2.  
  3. mysql就是一个数据库,存数据用的东西,它就像一个文件夹,里面存着很多的excel表格,我们可以在表格里面写数据,存数据。但是如果我们要使用数据库,我们必须先要去连接它,你和他建立了连接关系,你才能操作它里面存放的数据。

模拟一个场景,开启两个线程:

  线程一: 连接数据库,这个线程需要等待一个信号,告诉我们双方之间的网络是可以连通的.

  线程二:检测与数据库之间的网络是否联通,并发送一个可联通或者不可联通的信号.

  1. from threading import Thread,Event
  2. import threading
  3. import time,random
  4. def conn_mysql():
  5. count=1
  6. while not event.is_set():
  7. if count > 3:
  8. raise TimeoutError('链接超时') #自己发起错误
  9. print('<%s>第%s次尝试链接' % (threading.current_thread().getName(), count))
  10. event.wait(0.5) #
  11. count+=1
  12. print('<%s>链接成功' %threading.current_thread().getName())
  13.  
  14. def check_mysql():
  15. print('\033[45m[%s]正在检查mysql\033[0m' % threading.current_thread().getName())
  16. t1 = random.randint(0,3)
  17. print('>>>>',t1)
  18. time.sleep(t1)
  19. event.set()
  20. if __name__ == '__main__':
  21. event=Event()
  22. check = Thread(target=check_mysql)
  23. conn1=Thread(target=conn_mysql)
  24. conn2=Thread(target=conn_mysql)
  25.  
  26. check.start()
  27. conn1.start()
  28. conn2.start()

模拟连接的代码示例

三. 线程队列

queue队列: 使用import queue , 用法与进程Queue一样.

  queue.Queue(maxsize=0) -- 先进先出

  1. import queue #不需要通过threading模块里面导入,直接import queue就可以了,这是python自带的,用法基本和我们进程multiprocess中的queue是一样的
  2.  
  3. q=queue.Queue()
  4. q.put('first')
  5. q.put('second')
  6. q.put('third')
  7. # q.put_nowait() #没有数据就报错,可以通过try来搞
  8.  
  9. print(q.get())
  10. print(q.get())
  11. print(q.get())
  12. # q.get_nowait() #没有数据就报错,可以通过try来搞
  13.  
  14. # 执行结果: (先进先出)
  15. # first
  16. # second
  17. # third

先进先出示例代码

  queue.LifoQueue(maxsize=0) -- last in first out 后进先出

  1. import queue
  2.  
  3. q=queue.LifoQueue() #队列,类似于栈,后进先出的顺序
  4. q.put('first')
  5. q.put('second')
  6. q.put('third')
  7. # q.put_nowait()
  8.  
  9. print(q.get())
  10. print(q.get())
  11. print(q.get())
  12. # q.get_nowait()
  13.  
  14. # 执行结果:(后进先出)
  15. # third
  16. # second
  17. # first

后进先出示例代码

  queue.PriorityQueue(maxsize=0) -- 存储数据时可以设置优先级队列

  1. rt queue
  2.  
  3. q = queue.PriorityQueue() # 创建栈
  4.  
  5. # put()方法放置一个元组进入栈,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
  6. q.put((-10, "a"))
  7. q.put((-5, "a")) # 负数也可以
  8. # q.put((20,"ws")) # 如果两个值的优先级一样,那么按照后面的值的acsii码顺序来排序,如果字符串第一个数元素相同,比较第二个元素的acsii码顺序
  9. # q.put((20,"wd"))
  10. # q.put((20,{"a": 11})) # TypeError: unorderable types: dict() < dict() 不能是字典
  11. # q.put((20,("w", 1))) # 优先级相同的两个数据,他们后面的值必须是相同的数据类型才能比较,可以是元祖,也是通过元素的ascii码顺序来排序
  12.  
  13. q.put((20, "b"))
  14. q.put((20, "a"))
  15. q.put((0, "b"))
  16. q.put((30, "c"))
  17.  
  18. print(q.get())
  19. print(q.get())
  20. print(q.get())
  21. print(q.get())
  22. print(q.get())
  23. print(q.get())
  24.  
  25. # 数字越小优先级越高,优先级高的优先出队

优先级队列示例代码

四. 线程池

早期的时候并没有线程池,现在Python提供了一个新的标准或者说内置的模块,这个模块里面提供了新的线程池和进程池.

  模块介绍:

  1. # concurrent.futures模块提供了高度封装的异步调用接口
  2. # ThreadPoolExecutor: 线程池,提供异步调用
  3. # ProcessPoolExecutor: 进程池,提供异步调用
  4.  
  5. # 基本方法:
  6. # submit(func, *args, **kwargs) -- 异步提交任务
  7.  
  8. # map(func, *iterables, timeout=None, chunksize=1) -- 取代for循环submit的操作
  9.  
  10. # shutdown(wait=True) -- 相当于进程池的pool.close()和pool.join()操作
  11. 需要注意的是:
  12. wait=True,等待池内所有任务执行完毕回收完资源后才继续
  13. wait=False,立即返回,并不会等待池内的任务执行完毕
  14. 但不管wait参数为何值,整个程序都会等到所有任务执行完毕
  15. submitmap必须在shutdown之前
  16.  
  17. # result(timeout=None) -- 取得结果,相当于get(),如果没有值可取会阻塞住
  18.  
  19. # add_done_callback(func) -- 回调函数

  ThreadPoolExecutor的简单使用

  1. import time
  2. from concurrent.futures import ThreadPoolExecutor
  3.  
  4. def func(n):
  5. time.sleep(2)
  6. return n**2
  7.  
  8. if __name__ == '__main__':
  9. thread_pool = ThreadPoolExecutor(max_workers=4) # 创建线程池对象,默认一般开启的线程数量不超过CPU个数的5倍
  10. t_list = []
  11. for i in range(20):
  12. t = thread_pool.submit(func, i) # 提交执行函数,返回一个结果对象,i作为任务函数的参数.submit(func, *args, **kwargs)可以传递任意形式的参数
  13. t_list.append(t)
  14.  
  15. # thread_pool.shutdown() # shutdown()的作用相当于close()和join(),等待所有的线程执行完毕
  16. # for tt in t_list:
  17. # print(">>>", tt.result())
  18.  
  19. # 也可以不用shutdown()方法,改换下面这种方式:
  20. for n, tt in enumerate(t_list): # enumerate()枚举
  21. print(">>>", n, tt.result())
  22. time.sleep(2)

ThreadPoolExecutor的简单使用

  补充: ProcessPoolExecutor的使用

  1. 只需要将这一行代码改为下面这一行就可以了,其他的代码都不用变
  2. tpool = ThreadPoolExecutor(max_workers=5) #默认一般起线程的数据不超过CPU个数的5倍
  3. # tpool = ProcessPoolExecutor(max_workers=5)
  4.  
  5. 你就会发现为什么将线程池和进程池都放到这一个模块里面了,用法一样
  1. from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
  2. import threading
  3. import time, random
  4.  
  5. def task(n):
  6. print("{} is runing".format(threading.get_ident())) # 子线程ID号
  7. time.sleep(random.randint(1, 3))
  8. return n**2
  9.  
  10. if __name__ == '__main__':
  11. executor = ThreadPoolExecutor(max_workers=3) # 创建线程池对象,设置的线程数量为3
  12. for i in range(11):
  13. future = executor.submit(task, i)
  14. s = executor.map(task, range(1, 5)) # map()取代了 for循环+submit()
  15. print([i for i in s])

map的使用

  1. import time
  2. from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
  3.  
  4. def func(n):
  5. time.sleep(2)
  6. return n*n
  7.  
  8. def call_back(m):
  9. print("结果为:{}".format(m.result()))
  10.  
  11. if __name__ == '__main__':
  12. tpool = ThreadPoolExecutor(max_workers=5) # 创建进程池对象
  13. t_list = []
  14. for i in range(5):
  15. t = tpool.submit(func, i).add_done_callback(call_back)

回调函数的简单应用

  1. from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
  2. from multiprocessing import Pool
  3. import requests
  4. import json
  5. import os
  6.  
  7. def get_page(url):
  8. print('<进程%s> get %s' %(os.getpid(),url))
  9. respone=requests.get(url)
  10. if respone.status_code == 200:
  11. return {'url':url,'text':respone.text}
  12.  
  13. def parse_page(res):
  14. res=res.result()
  15. print('<进程%s> parse %s' %(os.getpid(),res['url']))
  16. parse_res='url:<%s> size:[%s]\n' %(res['url'],len(res['text']))
  17. with open('db.txt','a') as f:
  18. f.write(parse_res)
  19.  
  20. if __name__ == '__main__':
  21. urls=[
  22. 'https://www.baidu.com',
  23. 'https://www.python.org',
  24. 'https://www.openstack.org',
  25. 'https://help.github.com/',
  26. 'http://www.sina.com.cn/'
  27. ]
  28.  
  29. # p=Pool(3)
  30. # for url in urls:
  31. # p.apply_async(get_page,args=(url,),callback=pasrse_page)
  32. # p.close()
  33. # p.join()
  34.  
  35. p=ProcessPoolExecutor(3)
  36. for url in urls:
  37. p.submit(get_page,url).add_done_callback(parse_page) #parse_page拿到的是一个future对象obj,需要用obj.result()拿到结果

回调函数的应用

五. GIL锁

参考资料

Python 线程----线程方法,线程事件,线程队列,线程池,GIL锁,协程,Greenlet的更多相关文章

  1. GIL线程全局锁 协程

    GIL线程全局锁 线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程.对于io密集型任务 ...

  2. Python之路(第四十七篇) 协程:greenlet模块\gevent模块\asyncio模块

    一.协程介绍 协程:是单线程下的并发,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的. 协程相比于线程,最大的区别在于 ...

  3. Python--线程队列(queue)、multiprocessing模块(进程对列Queue、管道(pipe)、进程池)、协程

    队列(queue) 队列只在多线程里有意义,是一种线程安全的数据结构. get与put方法 ''' 创建一个“队列”对象 import queue q = queue.Queue(maxsize = ...

  4. python 线程(其他方法,队列,线程池,协程 greenlet模块 gevent模块)

    1.线程的其他方法 from threading import Thread,current_thread import time import threading def f1(n): time.s ...

  5. 线程队列 concurrent 协程 greenlet gevent

    死锁问题 所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进 ...

  6. day 34 线程队列 线程池 协程 Greenlet \Gevent 模块

    1 线程的其他方法 threading.current_thread().getName()    查询当前线程对象的名字 threading.current_thread().ident      ...

  7. day35:线程队列&进程池和线程池&回调函数&协程

    目录 1.线程队列 2.进程池和线程池 3.回调函数 4.协程:线程的具体实现 5.利用协程爬取数据 线程队列 1.线程队列的基本方法 put 存 get 取 put_nowait 存,超出了队列长度 ...

  8. python爬虫-使用线程池与使用协程的实例

    背景:爬取豆瓣电影top250的信息 使用线程池 import re from concurrent.futures import ThreadPoolExecutor import requests ...

  9. Python之路-python(Queue队列、进程、Gevent协程、Select\Poll\Epoll异步IO与事件驱动)

    一.进程: 1.语法 2.进程间通讯 3.进程池 二.Gevent协程 三.Select\Poll\Epoll异步IO与事件驱动 一.进程: 1.语法 简单的启动线程语法 def run(name): ...

随机推荐

  1. NOI2018 你的名字——SAM+线段树合并

    题目链接在这里洛谷/LOJ 题目大意 有一个串\(S\),每次询问给你一个串\(T\),两个数\(L\)和\(R\),问你\(T\)有多少个本质不同的子串不是\(S[L,R]\)的子串 SOLUTIO ...

  2. kudu_CM安装准备工作

    Cloudera Manager简介: hadoop: https://yq.aliyun.com/articles/60759 ----------------------------------- ...

  3. Mysql-sql行转列

    原始数据如下图所示:(商品的销售明细)date=业务日期:Item=商品名称:saleqty=销售数量 -- 建立测试数据(表)create table test (Date varchar(10), ...

  4. 51nod 1989 竞赛表格 (爆搜+DP算方案)

    题意 自己看 分析 其实统计出现次数与出现在矩阵的那个位置无关.所以我们定义f(i)f(i)f(i)表示iii的出现次数.那么就有转移方程式f(i)=1+∑j+rev(j)=if(j)f(i)=1+\ ...

  5. Codeforces Round #587 (Div. 3) D. Swords

    链接: https://codeforces.com/contest/1216/problem/D 题意: There were n types of swords in the theater ba ...

  6. PHP处理base64编码字符串

    接收前端传过来的base64编码后的字符串, 如果是json字符串, 那么PHP使用file_get_contents('php://input'); 来接收. 本次这里是以post传参的形式传bas ...

  7. [Luogu] 计算系数

    https://www.luogu.org/problemnew/show/P1313#sub Answer = a ^ n * b ^ m * C(k, min(n,  m)) 这里用费马小定理求逆 ...

  8. Postman中的全局/环境/集合变量的使用及优先级

    变量的使用场景 Postman的变量主要用于参数化和关联 应用1: 常用变量可以按使用范围设置成集合或全局变量 应用2: 一套接口要在不同的环境上测试时, 可以新建两个环境,比如test环境和stag ...

  9. 【java设计模式】-02工厂模式

    工厂模式简述 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在工厂模式中,我们在创建对象时不会对客 ...

  10. AngularJs 拦截器,拦截请求

    问题前述 我在项目中遇到这样一个问题: 在Angular项目中,会有很多需要用户点击操作的地方,如果用户点击过快会产生多次相同请求,会吃服务器带宽,如果这是其他涉及钱有关的操作,这会产生一个致命的问题 ...