python之线程和进程(并发编程)
python的GIL
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
上面的核心意思就是,无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行
线程
1同步锁
2死锁, 递归锁
3:信号量和同步对象(了解)
4队列------生产者消费者模型
5进程
线程的基本调用
<python的线程与threading模块>
import threading # 线程
import time def Hi(num):
print('hello %d' % num)
time.sleep(3) if __name__ == '__main__':
# 第一个参数是要执行的函数名,第二个是函数的参数(必须是可迭代对象)
t1 = threading.Thread(target=Hi, args=(10, )) # 创建一个线程对象
t1.start() # 开启线程
t2 = threading.Thread(target=Hi, args=(9, )) # 创建一个线程对象
t2.start() # 开启线程
print('ending....')
# 这就是并发的现象 # 并行:指的是两个或者多个事件在同一时刻发生(同时调用多核)
# 并发:指的是两个或者多个事件在同一时间间隔内发生(在一个核内快速的切换)
第二种调用方式
import threading
import time class MyThread(threading.Thread):
def __init__(self,num):
threading.Thread.__init__(self)
self.num = num def run(self):#定义每个线程要运行的函数 print("running on number:%s" %self.num) time.sleep(3) if __name__ == '__main__': t1 = MyThread(1)
t2 = MyThread(2)
t1.start()
t2.start() print("ending......")
join和setDaemon
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 18-5-22 下午8:45
# @Author : LK
# @File : lesson2.py
# @Software: PyCharm import threading
import time def music():
print('begin listen music %s'%time.ctime())
# t = input('请输入内容>>>')
# time.sleep(3)
print('stop listen music %s'%time.ctime())
# print(t) def game():
print('begin to game %s'%time.ctime())
time.sleep(5)
print('stop to game %s'%time.ctime()) if __name__ == '__main__':
t1 = threading.Thread(target=music)
t2 = threading.Thread(target=game) t1.start()
t2.start() # t1.join() # join就是等待的意思,让该线程执行完毕后,在执行主线程
# t2.join() # 注意如果注释这一句,结果是什么 print('ending....') print(t1.getName()) # 获取线程名,
join
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 18-5-22 下午9:26
# @Author : LK
# @File : 守护线程.py
# @Software: PyCharm # 守护线程 就是:和主线程一起退出,如果主线程结束了,那么不管守护的线程,有没有执行完毕,都退出
import threading
import time def music():
print('begin listen music %s' % time.ctime())
time.sleep(3)
print('stop listen music %s' % time.ctime()) def game():
print('begin to game %s' % time.ctime())
time.sleep(5)
print('stop to game %s' % time.ctime()) t1 = threading.Thread(target=music)
t2 = threading.Thread(target=game)
threads = []
threads.append(t1)
threads.append(t2)
if __name__ == '__main__': # t1.setDaemon(True)
t2.setDaemon(True)
for t in threads:
t.start()
# t.setDaemon(True) # 守护线程, 就是和主线程一块结束
print('ending....')
setDaemon
join():在子线程完成运行之前,这个子线程的父线程将一直被阻塞。
setDaemon(True):
将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。这个方法基本和join是相反的。
当我们 在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就分兵两路,分别运行,那么当主线程完成
想退出时,会检验子线程是否完成。如 果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是 只要主线程
完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 用setDaemon方法啦
其他方法
# run(): 线程被cpu调度后自动执行线程对象的run方法
# start():启动线程活动。
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。 threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
同步锁lock
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 18-5-23 下午4:42
# @Author : LK
# @File : 同步锁.py
# @Software: PyCharm
import threading, time # 用多线程去调用一个每次减1的函数,里面让他停留一会就会切换cpu,模拟线程安全问题,引出同步锁(互斥锁) def sub():
global num
# 这样坐是0,因为num-=1 这个执行的很快,没有到达时间片切换就执行完了,每次取的都是不同的值
# num -= 1
# 如果这里让他停一会,就会发生,前面的一些拿到的值为100,然后开始停下来,让其他线程取值
# 取得值还可能是100, 再过几个线程,取的时候就是前面的线程执行完了(到达时间片切换了)
# 取得是就可能是其他值(前面几个线程执行后的结果),最终结果就不在是0
# 这就是线程安全问题,多个线程都在处理同一个数据,
# 如果处理的太慢(sleep)就会发生多个线程处理的值都是原始的值(多个线程处理一个值) # 处理方法: 就是在执行这三条语句时,不让cpu切换,知道处理完成时,再切换(同步锁)
'''
定义一个锁:lock = threading.Lock()
lock.acquire() # 锁起来(获取)
这里是要执行的语句,
lock.release() #释放
这里的不执行完,不切换cpu
'''
lock.acquire()
temp = num
time.sleep(0.001)
num = temp - 1
lock.release() num = 100
l = []
lock = threading.Lock()
for i in range(100):
t = threading.Thread(target=sub)
l.append(t)
t.start() for t in l:
t.join()
print(num)
同步锁(互斥锁)
死锁(递归锁)
# 产生死锁:
'''
第一个线程执行doLockA, 然后lockB, 执行完actionA, 都释放了
开始执行actionB,对B上锁,不允许切换cpu,与此同时第二个线程开始执行actionA,对A上锁
不允许切换cpu,两个线程同时需要对方的资源释放但是都没有释放,所以死锁
(就像你问我要个香蕉, 我问你要个苹果, 你说我先给你,我说你先给我,于是就死锁了)
'''
'''
解决方法:
用threading.RLock() 去替换锁A和锁B
rlock就是在内部实现一个计数器,在同一个线程中,加锁就+1,释放就减一,只要锁数大于0,
其他线程就不能申请锁,
就是执行过程就是:当A函数执行完之后,所有线程开始抢占资源,谁抢到谁开始执行,
比如线程2抢到, 线程2开始执行A函数,A没有执行完之前其他线程不能够继续执行
'''
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 18-5-23 下午8:33
# @Author : LK
# @File : 递归锁_死锁.py
# @Software: PyCharm
import threading, time # 产生死锁:
'''
第一个线程执行doLockA, 然后lockB, 执行完actionA, 都释放了
开始执行actionB,对B上锁,不允许切换cpu,与此同时第二个线程开始执行actionA,对A上锁
不允许切换cpu,两个线程同时需要对方的资源释放但是都没有释放,所以死锁
(就像你问我要个香蕉, 我问你要个苹果, 你说我先给你,我说你先给我,于是就死锁了)
''' class myThread(threading.Thread):
def actionA(self):
lockA.acquire()
print(self.name, 'doLockA', time.ctime())
time.sleep(3)
lockB.acquire()
print(self.name, 'doLockB', time.ctime())
lockB.release()
lockA.release() def actionB(self):
lockB.acquire()
print(self.name, 'doLockB', time.ctime())
time.sleep(1)
lockA.acquire()
print(self.name, 'doLockA', time.ctime())
lockA.release()
lockB.release() def run(self):
self.actionA()
time.sleep(0.5)
self.actionB() if __name__ == '__main__':
lockA = threading.Lock()
lockB = threading.Lock()
r_lock = threading.RLock()
'''
解决方法:
用threading.RLock() 去替换锁A和锁B
rlock就是在内部实现一个计数器,在同一个线程中,加锁就+1,释放就减一,只要锁数大于0,
其他线程就不能申请锁,
就是执行过程就是:当A函数执行完之后,所有线程开始抢占资源,谁抢到谁开始执行,
比如线程2抢到, 线程2开始执行A函数,A没有执行完之前其他线程不能够继续执行
''' threads = []
for i in range(5):
'''创建5个线程对象'''
threads.append(myThread())
for t in threads:
t.start()
for i in threads:
t.join()
print('ending.....')
死锁(递归锁)
信号量与同步对象
信号量, 也是一种锁的机制, 在那个递归锁中的同时事件的安全问题中
里面是多个线程同时抢占资源, 但是这个是只允许指定的个数,去抢占
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 18-5-24 下午4:45
# @Author : LK
# @File : 信号量.py
# @Software: PyCharm '''
信号量, 也是一种锁的机制, 在那个递归锁中的同时事件的安全问题中
里面是多个线程同时抢占资源, 但是这个是只允许指定的个数,去抢占
'''
import threading, time
class myThread(threading.Thread):
def run(self):
if semaphore.acquire():
print(self.name)
time.sleep(3)
semaphore.release() if __name__ == '__main__': # 信号量, 参数的意思是同时允许几个线程去执行
semaphore = threading.Semaphore(5) threads = []
for i in range(10):
threads.append(myThread()) for t in threads:
t.start()
for t in threads:
t.join()
信号量
同步对象
event
'''
event 同步对象标志位,就是在一个线程中设定后,在其他线程中也能捕获到
需求:一个boss类,一个worker类,
当boss对象执行后,worker才执行,就需要设置一个同步的标志位,用来判断
该执行那个线程
'''
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 18-5-23 下午9:38
# @Author : LK
# @File : 同步对象.py
# @Software: PyCharm '''
event 同步对象标志位,就是在一个线程中设定后,在其他线程中也能捕获到
需求:一个boss类,一个worker类,
当boss对象执行后,worker才执行,就需要设置一个同步的标志位,用来判断
该执行那个线程
'''
'''
执行过程:
刚开始有5个worker线程和1个boss线程执行,
当worker执行时,wait没有被设定,一直在阻塞,但是boss可以执行
于是设定event并开始等5秒, 同时5个worker中的标志位变了,可以继续执行了
1秒后,worker清除event标志,继续阻塞,又过了4秒boss又设置了,
同时worker又可以执行了
'''
import threading, time
class Boss(threading.Thread):
def run(self):
print(self.name,'boss今天大家要加班到22点')
print(event.isSet()) # false,判断是否标志
event.set()
time.sleep(5)
print(self.name,'boss: <22:00>可以下班了')
print(event.isSet())
event.set() class Worker(threading.Thread):
def run(self):
event.wait() # 一旦event被设定 就等同于pass, 不设定就阻塞等待被设定
print(self.name,'worker:哎呀生活苦呀,')
time.sleep(1)
event.clear()
event.wait()
print(self.name,'worker: oyear下班了') # 如果不用event的话,就可能导致worker先说话,但是需求是让boss先说话,
# 因为cpu是抢占的方式执行的,不会按照需求走,所以就要引入同步标志位event
if __name__ == '__main__':
event = threading.Event()
threads =[] # 创建5个worker线程,1个boss线程
for i in range(5):
threads.append(Worker())
threads.append(Boss()) for t in threads:
t.start()
for t in threads:
t.join()
print('ending....')
同步对象
队列----多线程常用方法
队列的引入
'''
用多个线程同时对列表进行删除最后一个值,
会报错, 是因为可能会有多个线程同时取到最后一个值,都进行删除,
就会报错, 解决方法:可以用锁,将其锁住,(相当于串行的执行), 或者用队列
'''
'''
l = [1,2,3,4,5]
def fun():
while 1:
if not l:
break
a = l[-1]
print(a)
time.sleep(1)
l.remove(a)
if __name__ == '__main__':
t1 = threading.Thread(target=fun, args=())
t2 = threading.Thread(target=fun, args=())
t1.start()
t2.start()
'''
队列的引入
Python Queue模块有三种队列及构造函数:
1、Python Queue模块的FIFO队列先进先出。 class queue.Queue(maxsize)
2、LIFO类似于堆,即先进后出。 class queue.LifoQueue(maxsize)
3、还有在一种是优先级队列级别越低越先出来。 class queue.PriorityQueue(maxsize)
import queue # 创建一个队列,里面最多可以存放3个数据
q = queue.Queue(maxsize=3) # FIFO,先进先出
# 将一个值入队
q.put(10)
q.put(9)
q.put(8)
# 如果这里在加上一个put, 就是有4个数据入队,但是空间不够,就会一直阻塞,直到,队列有空间时(从队列中取出get)
# block参数默认是True, 如果改成False,当队列满的时候如果继续入队,就不会堵塞而是报错queue.Full
# q.put(9, block=False)
while 1:
'''
将一个值从队列中取出
q.get()
调用队列对象的get()
方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,
get()
就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。
'''
# 取完之后将会阻塞, block=Fales,如果队列为空时,继续出队,就会报错,queue.Empty
# get类似与put
data = q.get(block=False)
print(data)
调用一
# 先进后出 LIFO 类似与栈
import queue
# 先进后出 LIFO 类似与栈
q = queue.LifoQueue(maxsize=4)
q.put('你好')
q.put(123)
q.put({"name":"lucky"}) while 1:
data = q.get()
print(data)
调用二
# 还有在一种是优先级队列级别越低越先出来1级最低。class queue.PriorityQueue(maxsize)
import queue
q = queue.PriorityQueue(maxsize=3)
q.put([2,'你好'])
q.put([3,123,'aa'])
q.put([1,{"name":"lucky"}]) while 1:
data = q.get()
# print(data)
print(data[1]) # 只显示数据,
调用三
import queue
q = queue.Queue(maxsize=4)
q.put('a')
q.put('b')
q.put('c')
print(q.qsize()) # 打印当前队列的大小,现在队列中有几个值
print(q.full()) # 判断是否满
print(q.empty()) # 判断是否为空
# q.put_nowait(3) # 相当于q.put(3, block=Flase)
# q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
# q.join() 实际上意味着等到队列为空,再执行别的操作
while 1:
data = q.get()
print(data)
print(q.qsize()) # 打印当前队列的大小,现在队列中有几个值
print(q.full()) # 判断是否满
print(q.empty()) # 判断是否为空
队列的其他方法
进程的基本使用
多进程模块 multiprocessing
multiprocessing
进程的使用和线程的使用和调用的方法基本一样
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 18-5-25 下午5:31
# @Author : LK
# @File : 测试进程和线程.py
# @Software: PyCharm
from threading import Thread
from multiprocessing import Process
import time def fun():
print(time.ctime())
if __name__ == '__main__':
threads = []
for i in range(1000):
# t = Thread(target=fun) # 线程
t = Process(target=fun) # 进程
threads.append(t)
for t in threads:
t.start()
for t in threads:
t.join()
from multiprocessing import Process
import time def f(name):
time.sleep(1)
print('hello %s'%name, time.ctime()) if __name__ == '__main__':
p_list = []
for t in range(3):
p = Process(target=f, args=('luck',))
p_list.append(p) # 三个进程时在同一时刻开启的
for p in p_list:
p.start()
for p in p_list:
p.join() print('ending....')
进程的调用方法一
class myProcess(Process):
def __init__(self):
# 父类初始化, 不传参数不用写
super(myProcess, self).__init__()
def run(self):
time.sleep(1)
print(self.name,'hello ',time.ctime()) if __name__ == '__main__':
p_list = []
for i in range(3):
p_list.append(myProcess())
for p in p_list:
p.start()
for p in p_list:
p.join()
进程的调用方法二
查看进程号
# 查查看进程的id
# os.getppid()获得当前进程的父进程,os.getpid()获得当前进程
# os.getppid 是pycharm的进程号
'''
from multiprocessing import Process
import os
import time def info(title):
print("title:", title)
print('父进程 id:', os.getppid())
print('当前进程 id:', os.getpid())
# time.sleep(3) if __name__ == '__main__':
info('主进程')
time.sleep(1)
print("------------------")
p = Process(target=info, args=('lucky子进程',))
p.start()
p.join()
p_list = []
for i in range(3):
p = Process(target=info, args=('子进程%d' % i,))
p_list.append(p)
for p in p_list:
p.start()
for p in p_list:
p.join()
查看进程号,进程之间的关系理解
Process 类
构造方法: Process([group [, target [, name [, args [, kwargs]]]]]) group: 线程组,目前还没有实现,库引用中提示必须是None;
target: 要执行的方法;
name: 进程名;
args/kwargs: 要传入方法的参数。 实例方法: is_alive():返回进程是否在运行。 join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。 start():进程准备就绪,等待CPU调度 run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。 terminate():不管任务是否完成,立即停止工作进程 属性: daemon:和线程的setDeamon功能一样 name:进程名字。 ''' from multiprocessing import Process
import time class myProcess(Process):
def __init__(self, args):
super(myProcess,self).__init__()
self.args = args
def fun(self):
time.sleep(1)
print(self.name, self.is_alive(), self.args, self.pid)
time.sleep(1) def run(self):
self.fun() if __name__ == '__main__':
p_list = []
for i in range(10):
# p = Process(target=fun, args=('子进程%d' % i,))
p_list.append(myProcess('子进程%d' % i))
# p_list[-2].daemon = True
# 因为这三个进程是同时执行的,所以这个属性不明显
for p in p_list:
p.start()
for p in p_list:
p.join()
进程模块的其他方法
进程间的通信
队列,这里的队列和线程里的队列调用方法不一样
# 什么是进程通信,为什么要进程通信
# 因为进程与进程之间是相互独立的,不像线程可以信息共享,要想让进程间相互信息共享,就要传参数(利用队列) from multiprocessing import Process, Queue # 进程队列
import time
import queue # 线程队列 # 主进程get数据,子进程put数据,
# 阻塞的原因是,这个队列的数据不共享,在子进程中队列和主进程的队列信息不共享,没有任何关系
# 结局方法,将队列作为参数传过去
# 注意在win上面不传参数不能运行,但是在linux下能运行
# 所以轻易不要用多进程,进行进程间通信时,会涉及到copy资源
# def fun(q):
# ''' 队列
def fun():
q.put(1)
q.put(2)
q.put(3)
print(id(q))
# print(q.empty())
# def fun2():
def fun2(q):
while 1:
# print(q.empty())
data = q.get()
print(data)
print(id(q)) if __name__ == '__main__':
# q = queue.Queue() # 线程队列,
q = Queue() # 进程队列
# p = Process(target=fun, args=(q,)) # 传参进行了copy占用资源
p = Process(target=fun)
p.start()
p.join()
p2 = Process(target=fun2)
p2 = Process(target=fun2, args=(q,))
p2.start()
p2.join()
管道
from multiprocessing import Pipe
def fun(child_conn):
child_conn.send([12, {"name":"yuan"}, 'hello'])
print('主进程说:',child_conn.recv())
print('子进程id',id(child_conn)) if __name__ == '__main__':
prepare_conn, child_conn = Pipe() # 双向管道
print('id:',id(prepare_conn), id(child_conn))
p = Process(target=fun, args=(child_conn,))
p.start()
print('子进程说:',prepare_conn.recv())
prepare_conn.send('你好')
print('主进程id',id(prepare_conn))
p.join()
Managers
Queue和pipe只是实现了数据交互,并没有实现数据共享,即一个进程去更改另外一个进程的数据
Managers支持的数据类型
# A manager returned by Manager() will support types list, dict, Namespace,
# Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier,
# Queue, Value and Array. For example: from multiprocessing import Manager
# Queue和pipe只是实现了数据交互,并没实现数据共享,即一个进程去更改另一个进程的数据。
# 所以利用Managers 进行数据共享, 同样也需要传参 def fun(d, l, i):
d[i]='字典'+ str(i)
l.append(i)
# pass
if __name__ == '__main__':
with Manager() as manager:
d = manager.dict() # d = {} 创建一个字典
l = manager.list(range(5)) # l = range(5)
p_list = []
for i in range(5):
p = Process(target=fun,args=(d,l,i,))
p.start()
p_list.append(p)
for p in p_list:
p.join() print(d)
print(l)
Managers模块
进程同步
# 进程锁, lock
from multiprocessing import Process, Lock
import time def fun(i):
lock.acquire()
print('%d'%i)
lock.release()
if __name__ == '__main__':
lock = Lock()
for i in range(10):
p = Process(target=fun, args=(i,)).start()
进程同步,锁
进程池
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,
如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有两个方法:
apply 同步
apply_async 异步(常用)
在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。多进程是实现并发的手段之一,需要注意的问题是:
- 很明显需要并发执行的任务通常要远大于核数
- 一个操作系统不可能无限开启进程,通常有几个核就开几个进程
- 进程开启过多,效率反而会下降(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行)
例如当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个。。。手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。
我们就可以通过维护一个进程池来控制进程数目,比如httpd的进程模式,规定最小进程数和最大进程数...
ps:对于远程过程调用的高级应用程序而言,应该使用进程池,Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,就重用进程池中的进程。
'''
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,
如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有两个方法:
apply 同步
apply_async 异步(常用)
'''
from multiprocessing import Process, Pool
import time, os def foo(i):
time.sleep(1)
print('子进程:',os.getpid(),i)
return 'Hello %s'%i
def bar(args):
# print('',os.getpgid())
print(args)
print('主进程:',os.getpid()) if __name__ == '__main__':
pool = Pool(5) # 进程池中开5个进程, 有100个任务需要执行, 每次执行5个
print('main :',os.getpid())
for i in range(100):
# 回调函数:某个动作或者 函数执行成功后,再去执行的函数
# pool.apply_async(func=foo, args=(i,))
# bar作为回调函数, 每次执行一个进程后都会执行回调函数, 回调函数是主进程中调用的
# func中函数的返回值,传给回调函数做参数
pool.apply_async(func=foo, args=(i,), callback=bar)
# close和join必须加,而且顺序固定
pool.close()
pool.join()
进程池
回调函数
回掉函数:
需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数
我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果。
from multiprocessing import Pool
import requests
import json
import os def get_page(url):
print('<进程%s> get %s' %(os.getpid(),url))
respone=requests.get(url)
if respone.status_code == 200:
return {'url':url,'text':respone.text} def pasrse_page(res):
print('<进程%s> parse %s' %(os.getpid(),res['url']))
parse_res='url:<%s> size:[%s]\n' %(res['url'],len(res['text']))
with open('db.txt','a') as f:
f.write(parse_res) if __name__ == '__main__':
urls=[
'https://www.baidu.com',
'https://www.python.org',
'https://www.openstack.org',
'https://help.github.com/',
'http://www.sina.com.cn/'
] p=Pool(3)
res_l=[]
for url in urls:
res=p.apply_async(get_page,args=(url,),callback=pasrse_page)
res_l.append(res) p.close()
p.join()
print([res.get() for res in res_l]) #拿到的是get_page的结果,其实完全没必要拿该结果,该结果已经传给回调函数处理了
进程池应用
协程
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 18-5-26 上午10:19
# @Author : LK
# @File : 协程.py
# @Software: PyCharm
from greenlet import greenlet # 优缺点
'''
def test1():
print(12)
g2.switch() # 切换到g2
print(24)
g2.switch() # 切换到g2
def test2():
print(56)
g1.switch() # 切换到g1
print(65)
g1.switch() if __name__ == '__main__':
g1 = greenlet(test1)
g2 = greenlet(test2)
g2.switch() '''
'''
import gevent import requests,time start=time.time() def f(url):
print('GET: %s' % url)
resp =requests.get(url)
data = resp.text
f = open('new.html', 'w', encoding='utf-8')
f.write(data)
f.close()
print('%d bytes received from %s.' % (len(data), url)) # 这就相当于一个协程, 每次进行读写操作阻塞时,都会切换cpu
gevent.joinall([ gevent.spawn(f, 'https://www.python.org/'),
gevent.spawn(f, 'https://www.yahoo.com/'),
gevent.spawn(f, 'https://www.baidu.com/'),
gevent.spawn(f, 'https://www.sina.com.cn/'), ]) # f('https://www.python.org/')
#
# f('https://www.yahoo.com/')
#
# f('https://baidu.com/')
#
# f('https://www.sina.com.cn/') print("cost time:",time.time()-start) '''
# import threading, gevent
# def fun():
# a = input('>>>')
# print(a)
# def fun2():
# print('\nhello')
# # gevent.joinall([
# #
# # gevent.spawn(fun()),
# # gevent.spawn(fun2()),
# # ])
# if __name__ == '__main__':
# # t1 = threading.Thread(target=fun).start()
# # t2 = threading.Thread(target=fun2).start()
# gevent.joinall([
#
# gevent.spawn(fun),
# gevent.spawn(fun2),
# ])
未完整
协程参考:
http://www.cnblogs.com/linhaifeng/articles/7429894.html
协程,协作式----- 非抢占式的程序
自己调动
Yield(协程)
用户态的切换呢
关键点在哪一步切换
协程主要解决的是io操作
协程:本质上就是一个线程
协程的优势:
1:没有切换的消耗
2:没有锁的概念
但是不能用多核,所以用多进程+协程,是一个很好的解决并发的方案
驱动事件
@font-face{ font-family:"Times New Roman"; } @font-face{ font-family:"宋体"; } p.MsoNormal{ mso-style-name:正文; mso-style-parent:""; margin:0pt; margin-bottom:.0001pt; mso-pagination:none; text-align:left; font-family:'Times New Roman'; font-size:12.0000pt; mso-font-kerning:1.0000pt; } span.msoIns{ mso-style-type:export-only; mso-style-name:""; text-decoration:underline; text-underline:single; color:blue; } span.msoDel{ mso-style-type:export-only; mso-style-name:""; text-decoration:line-through; color:red; } @page{mso-page-border-surround-header:no; mso-page-border-surround-footer:no;}@page Section0{ } div.Section0{page:Section0;}
Io多路复用
Select 触发方式
1:水平处罚
2:边缘触发
3:IO多路复用优势:同时可以监听多个链接
Select 负责监听
IO多路复用:
Select 那个平台都有
Poll
Epoll 效率最高
python之线程和进程(并发编程)的更多相关文章
- 操作系统/应用程序、操作中的“并发”、线程和进程,python中线程和进程(GIL锁),python线程编写+锁
并发编程前言: 1.网络应用 1)爬虫 直接应用并发编程: 2)网络框架 django flask tornado 源码-并发编程 3)socketserver 源码-并发编程 2.运维领域 1)自动 ...
- Python之线程、进程和协程
python之线程.进程和协程 目录: 引言 一.线程 1.1 普通的多线程 1.2 自定义线程类 1.3 线程锁 1.3.1 未使用锁 1.3.2 普通锁Lock和RLock 1.3.3 信号量(S ...
- Python 中线程和进程
目录 线程和进程 一. 什么是进程 / 线程 1. 引论 2. 线程 3. 进程 4. 区别 5. 使用 二. 多线程使用 1. 常用方法 2. 常用参数 3. 多线程的应用 3.1 重写线程法 3. ...
- Python之线程与进程
今天我们来了解一下Python的线程和进程的管理机制 首先,我们要了解下线程跟进程的概念: 线程(Thread)是操作系统能够进行运算调度的最小的单位,是一堆cpu的指令.他被包含在进程中,是进程中的 ...
- python中线程和进程的简单了解
python中线程和进程的简单了解 一.操作系统.应用程序 1.硬件:硬盘.cpu.主板.显卡........ 2.装系统(本身也是一个软件): 系统就是一个由程序员写出来的软件,该软件用于控制计 ...
- Python自动化 【第九篇】:Python基础-线程、进程及python GIL全局解释器锁
本节内容: 进程与线程区别 线程 a) 语法 b) join c) 线程锁之Lock\Rlock\信号量 d) 将线程变为守护进程 e) Event事件 f) queue队列 g) 生 ...
- python之线程、进程
线程语法 class Thread(_Verbose): """A class that represents a thread of control. This cla ...
- Python基础—线程、进程和协程
今天已是学习Python的第十一天,来干一碗鸡汤继续今天的内容,今天的鸡汤是:超越别人对你的期望.本篇博客主要介绍以下几点内容: 线程的基本使用: 线程的锁机制: 生产者消费之模型(队列): 如何自定 ...
- python的线程和进程
1.线程的基本概念 概念 线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程 ...
随机推荐
- 8.Vue的slot
1.什么是slot 在 Vue.js 中我们使用 <slot> 元素作为承载分发内容的出口,作者称其为 插槽,可以应用在组合组件的场景中 2.使用 建立组件预留插槽 定义填充入插槽 ...
- [LeetCode] 215. Kth Largest Element in an Array 数组中第k大的数字
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the so ...
- linux写shell注意的问题
linux写shell注意的问题一定要vi crontab.sh来写 ps:在windows系统中编辑过这个文件,就会出现类似的换行符 这样导致linux系统中运行sh报错 比如会出现$MQ字符 如果 ...
- centos7上配置mysql8的双主互写
注意:1.主库1:10.1.131.75,主库2:10.1.131.762.server-id必须是纯数字,并且主从两个server-id在局域网内要唯一. [主节点1]vi /etc/my.cnf[ ...
- ArrayList 源码分析 基于jdk1.8:
1:数据结构: transient Object[] elementData; //说明内部维护的数据结构是一个Object[] 数组 成员属性: private static final int ...
- scala中nothing和null的区别
1:nothing是所有类型的子类,他没有具体的实例对象,常见的应用:抛出异常.程序exit.无线循环等. 2:nothing是所有类型的子类,也是null的子类,nothing没有对象,但是可以用来 ...
- logstash之mongodb-log
1.logstash6.5.3 配置收集mongodb的日志: 首先在mongodb服务器上部署filebeat收集日志并上传到logstash进行处理,然后上传到ES. filebeat-conf: ...
- WebStrom安装Markdown插件
安装步骤 File→Settings→Plugins→关键字搜索markdown→选择Markdown Navigator→点击Install→出现下载弹窗,等待下载完毕→重启Webstrom 效果预 ...
- 一个小巧,也很nice的“小日历”--一个Android App
一个小巧也很Nice的“小日历” 背景 因为,常用日历记一些事情,Android自带的日历,如果有事情,会显示一个小点,然后点击进去后才能看到事情的具体内容,不是很方便. 所以,写了一个“小日历” 特 ...
- 【05】Jenkins:用户权限管理
写在前面的话 在一个企业研发部门内部,可能存在多个运维人员,而这些运维人员往往负责不同的项目,但是有可能他们用的又是同一个 Jenkins 的不同用户.那么我们就希望实现一个需求,能够不同的用户登录 ...