第三部分:Semaphore控制进入数量的锁

  有时候可能需要运行多个工作线程同时访问一个资源,但要限制总数。例如,连接池支持同时连接,但是数目可能是固定的,或者一个网络应用可能支持固定数据的并发下载。这些连接就可以使用semaphore来进行管理。

import threading
import time class HtmlSpider(threading.Thread):
def __init__(self,url):
super().__init__()
self.url = url def run(self):
time.sleep()
print("got html text success") class UrlProducer(threading.Thread):
def run(self):
for i in range(): # 比如抓取20个网站信息
html_thread = HtmlSpider("http://baidu.com/{}".format(i))
html_thread.start() if __name__ == '__main__':
url_producer = UrlProducer()
url_producer.start()

  我们可以看到结果是20个并发去执行的,如果我们想一次并发3个线程如何处理呢?

  更改代码如下:

import threading
import time class HtmlSpider(threading.Thread):
def __init__(self,url,sem):
super().__init__()
self.url = url
self.sem = sem def run(self):
time.sleep()
print("got html text success")
self.sem.release() class UrlProducer(threading.Thread):
def __init__(self,sem):
super().__init__()
self.sem = sem def run(self):
for i in range(): # 比如抓取20个网站信息
self.sem.acquire()
html_thread = HtmlSpider("http://baidu.com/{}".format(i),self.sem)
html_thread.start() if __name__ == '__main__':
sem = threading.Semaphore()
url_producer = UrlProducer(sem)
url_producer.start()

  其实semaphore内部是调用了一个condition。我们注意semaphore也是必须有acquire方法和release方法。

  另外,我们发现Queue内部也是调用了很多condition的方法。

问:前面介绍了很多同步的方法,其他一些软件都有池的概念,Python也具备吗?

答:当然,Python有两个池子,一个叫线程池,一个叫进程池,后续我们讲到进程的时候回搠进程池。现在先说线程池。

  线程池就是concurrent模块包,是在Python3.2时候引入的。这个池是非常顶层的,对于我们进行线程和进程池编码是非好的。而且接口会高度的一致。

  一个问题:为什么要有线程池?

  目的很简单:就是非常容易的管理线程,线程池自己去调度新的线程去使用,线程池过大的时候会阻塞,知道最新的线程空出来。它不仅仅起到了数量控制,如果我们在主线程当中可以获取某一个线程的状态,或者某一个任务的状态,或者返回值,这样就会变得非常简单。另外,当一个线程完成的时候我们主线程就会立马知道。futures可以让多线程和多进程编码接口一直。

from concurrent.futures import ThreadPoolExecutor
import time def get_html(times):
time.sleep(times)
print("get page {} success".format(times))
return times executor = ThreadPoolExecutor(max_workers=)
# 通过submit函数提交执行的函数到线程池中,submit是立即返回
task1 = executor.submit(get_html,())
task2 = executor.submit(get_html,()) # done方法用于判定某个人物是否完成
print(task1.done()) # 判定我们的函数是否执行成功的
print(task2.cancel()) # 如我我们执行的状态是执行中是cancel不了的
time.sleep()
print(task1.done()) # 判定我们的函数是否执行成功的 # result方法可以获取task的执行结果
print(task1.result())

  运行结果:

False
False
get page success
get page success
True

  这里面我们用到了ThreadPoolExecutor的类,其中规定了运行的线程数量。

  done()方法:判定我们的函数是否执行成功

  result()方法:返回函数是否成功执行

  cancel()方法:取消一个线程任务(但是如果我们的任务是在执行中,是无法cancel掉的)

  另外,我们在想一下,我们想批量的进行提交并且知道提交是否成功怎么写。

  这个时候我们需要导入as_completed的模块,as_completed是一个生成器(我们知道生成器最好的方式就用for循环提取出来),我们再用比较高端的推导式的方式进行提交。

from concurrent.futures import ThreadPoolExecutor,as_completed
import time def get_html(times):
time.sleep(times)
print("get page {} success".format(times))
return times executor = ThreadPoolExecutor(max_workers=)
urls = [,,]
all_task = [executor.submit(get_html,(url)) for url in urls]
for future in as_completed(all_task):
data = future.result() #
print("get {} page success".format(data))

  另外,我们还可以通过executor本身的map方法来完成task

# 通过executor获取已经完成的task
for data in executor.map(get_html,urls):
print("get {} page success".format(data)) # get page success
# get page success
# get page success
# get page success
# get page success
# get page success

  但是,略有有点儿差别,上面是完成一个打印一个。

  再加一个wait等待。这个命令其实也是非常常用而且也是非常好的模块。wait模块是等待某一个函数结束再执行下面的内容。另外wait模块有有个一传参return_when=后面有四种方式:

  FIRST_COMPLETED 当地一个执行完毕

  FIRST_EXCEPTION

  ALL_COMPLETED

  _AS_COMPLETED

executor = ThreadPoolExecutor(max_workers=)
urls = [,,]
all_task = [executor.submit(get_html,(url)) for url in urls]
wait(all_task,return_when='FIRST_EXCEPTION')
print("main over")

  

  小结一下:

  * 这样关于线程池,我们知道最常用的三个模块:ThreadPoolExecutor, as_completed(注意是一个迭代器), wait。其中方法有submit,result,cancel,done等方法。wait也是可以传递参数的。

  * concurrent.futures 中的Future对象我们一般叫做未来对象,但实际上呢,更形象的说叫task返回容器,task执行结果都会放入里面。

问:线程的内容真是不少,功能也是不少,但是还是挺有规律的。

答:其实线程这块儿,还有几个内容,都非常简单,讲解完毕我们最线程进行总结,然后进入进程方面的讲解。

  补充1(threadLocal模块):我们发现如果两个线程同时操作一个函数的时候,会造成函数中的变量混乱的情况。我们可以通过threadLocal的方法,也叫做线程特定数据。给每一个线程单独去分配一个本地变量可以防止这个问题:代码如下。

import threading

num =
local = threading.local() def run(x,n):
x = x + n
x = x - n def func(n):
local.value = num
for i in range():
run(local.value,n)
print("%s--%d" %(threading.current_thread().getName(),local.value)) if __name__ == '__main__':
t1 = threading.Thread(target=func,args=(,))
t2 = threading.Thread(target=func,args=(,)) t1.start()
t2.start()
t1.join()
t2.join()
#
# Thread - - -
# Thread - - -

  

  补充2(barrier模块):这个单词是障碍的意思,也就是说像是一个“班车”凑够了多少个“人”才发车。这里就是凑够了多少个线程再进行线程计算,不过这个方法有一个维内托,如果数量不够时候,会一直停在那里等待线程。这种方法平时用的也不是很多。它的方法也是wait,代码如下:

import threading,time

bar = threading.Barrier()

def run():
print("{} -- start".format(threading.current_thread().getName()))
time.sleep()
bar.wait()
print("{} -- end".format(threading.current_thread().getName())) if __name__ == '__main__':
for i in range():
threading.Thread(target=run).start()

  我们发现:分配6个线程,其实给的是4个,线程6个并发了之后,等待2个结束并发,一直等不到,就停在那里了

  补充3(Timer模块):这个模块很好理解,就是控制线程并发的事件,这是一个定时器,这个定时的事件结束的时候再去开启。

import threading

def run():
print("Thomas is running") t = threading.Timer(,run) print("父线程开始......")
t.start()
t.join()
print("父线程结束......")

  

  补充4(Event模块):这个模块非常简单,我们使用手工的方式进行线程之间上锁解锁的方式进行通讯,我们可以调用线程的事件(因为线程行动本身就是一个事件),让上一个线程事件等待时间触发。和Condition模块非常的类似。

import threading,time

def func():
event = threading.Event()
def run():
for i in range():
event.wait()
event.clear()
print("Thomas is running")
threading.Thread(target=run).start() return event e = func()
for i in range():
e.set()
time.sleep()

  分析代码我们可以看出.wait是阻塞等待时间的触发。clear是重置的意思。set是设定的内容。

  补充5(enumerate模块):略

  总结:现在我们可以对Python的进程进行一下总结了。

  第一:进程是运行程序最小的操作单元。在IO操作的时候会经常用到。

  第二:Python本身具备GIL(全局解释器锁),所以在CPython的解释下,一个线程放入一个CPU下,在诸如PyPy的Python解释器下,就是一种去GIL话的解释器。

  第三:进程在上面的框架解释下,是线程交替来进行多线程操作的,系统无法自动的调配多核。

  第四:由于线程本身设计的原因,线程在运行程序后会按照自有的规则释放空间,由于这个释放空间的时间非常短暂,造成程序和程序,数据和数据之间可能产生混乱的情况。因此我们引入了锁、event、condition等方式进行控制。

  第五:线程有一些概念是成对出现的,正是由于第四条的情况。比如守护和阻塞(daemon和join),wait和clear(event事件),wait和notify(Condition条件),done和wait等。

  第六:线程分主线程和子线程这么一说,wait这个模块其实是属于小而精的一种阻塞操作方式,另外我们还可以用with语句来简化代码,用推导式直接进行推送任务到进程中。

  第七:平时我们也常用线程池来让Python自动推送任务到线程当中,submit就是这个动作。

  第八:诸如像ThreadLocal,Event,Timer,semaphore,barrier等小技巧也需要了解。

Python说文解字_Python之多任务_02的更多相关文章

  1. Python说文解字_Python之多任务_01

    Python 之 多任务: Python之多任务是现在多任务编程运用Python语言为载体的一种体现.其中涵盖:进程.线程.并发等方面的内容,以及包括近些年在大数据运算.人工智能领域运用强大的GPU运 ...

  2. Python说文解字_Python之多任务_05

    问:在Py3.5之前yield表现非常好,在Py3.5之后为了将予以变得更加明确,就引入了async和await关键词用于定义原生的协议. 答:async和await原生协程: async def d ...

  3. Python说文解字_Python之多任务_03

    问:线程学完了,现在我们开始学习进程了吧? 答:是的.前面说到线程就是我们的手,我们现在可以学习一下我们的“胳膊”了. 我们有了多线程,为什么还要学习多进程呢?这是因为在Python当中有一把GIL锁 ...

  4. Python说文解字_Python之多任务_04

    问:并发.并行.同步.异步.阻塞.非阻塞 答: 并发.并行: 并发是指一个时间段内(不是指的时间点),有几个程序在同一个CPU上运行,但是任意时刻只有一个程序在CPU上运行.对人类的时钟来说1秒钟能干 ...

  5. Python说文解字_详解元类

    1.深入理解一切接对象: 1.1 什么是类和对象? 首先明白元类之前要明白什么叫做类.类是面向对象object oriented programming的重要概念.在面向对象中类和对象是最基本的两个概 ...

  6. Python说文解字_杂谈05

    1. isinstance和type: is和==符号,is指的是内存地址,是不是一个对象,ID知否相同 集成链 class A: pass class B(A): pass b = B() prin ...

  7. Python说文解字_杂谈09

    1. 元类编程代码分析: import numbers class Field: pass class IntField(Field): # 数据描述符: # 初始化 def __init__(sel ...

  8. Python说文解字_杂谈08

    1. Python变量到底是什么? Python和Java中的变量本质不一样,python的变量实质是一个指针 int str,便利贴 a = 1 # 1. a贴在1上面 # 2. 它的过程是先生成对 ...

  9. Python说文解字_杂谈07

    1. 深入dict from collections.abc import Mapping,MutableMapping # dict 属于mapping类型 a = {} print(isinsta ...

随机推荐

  1. PHP 跨域之header

    之前的博客里记录了PHP解决跨域的方案:JSONP:https://www.cnblogs.com/pawn-i/p/11899120.html 除了jsonp之后,还是通过header函数设置响应头 ...

  2. [题解] LuoguP2764 最小路径覆盖问题

    传送门 好久没做网络流方面的题发现自己啥都不会了qwq 题意:给一张有向图,让你用最少的简单路径覆盖所有的点. 考虑这样一个东西,刚开始,我们有\(n\)条路径,每条路径就是单一的一个点,那么我们的目 ...

  3. HTTP和HTTPS的区别及HTTPS加密算法

    一.HTTP和HTTPS的概念              HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传 ...

  4. uniapp 小程序实现自定义底部导航栏(tarbar)

    在小程序开发中,默认底部导航栏很难满足实际需求,好在官方给出了自定义形式,效果如下: 话不多说,直接上代码 1.组件 custom-tarbar.vue文件 <template> < ...

  5. 转载-Logistic回归总结

     Logistic回归总结 作者:洞庭之子 微博:洞庭之子-Bing (2013年11月) 1.引言 看了Stanford的Andrew Ng老师的机器学习公开课中关于Logistic Regress ...

  6. 八十九、SAP中ALV事件之三,查看事件自带说明

    一.双击REUSE_ALV_GRID_DISPLAY,来到SE37的这个函数模块中,查看IT_EVENT的相关说明,点击后面的显示按钮 二.翻译第一句 三.翻译第二句 四.翻译第三句 五.翻译第四句 ...

  7. 154-PHP strpos函数

    <?php $str='passwords'; //定义一个字符串 $position=strpos($str,'s'); //查找字母s第一次出现的位置 echo '字母s的位置是'.$pos ...

  8. 150-PHP nl2br函数(一)

    <?php $str="h t m l"; //定义一个多处换行的字串 echo "未处理前的输出形式:<br />{$str}"; #nl2 ...

  9. 【CS224n-2019学习笔记】Lecture 1: Introduction and Word Vectors

    附上斯坦福cs224n-2019链接:https://web.stanford.edu/class/archive/cs/cs224n/cs224n.1194/ 文章目录 1.课程简单介绍 1.1 本 ...

  10. Spark 资源调度包 stage 类解析

    spark 资源调度包 Stage(阶段) 类解析 Stage 概念 Spark 任务会根据 RDD 之间的依赖关系, 形成一个DAG有向无环图, DAG会被提交给DAGScheduler, DAGS ...