一、Python进程

  IO密集型----多线程

  计算密集型----多进程

  1、单进程  

from multiprocessing import Process
def foo(i):
print('你好哈',i)
if __name__ == '__main__': #if __name__ == '__main__':只可做测试调用,不能用于生产,windows不支持,linux中可不用添加if __name__ == '__main__'
for i in range(10):
t = Process(target=foo,args=(i,)) #创建进程
t.start() #执行进程

  注意:由于进程之间的数据需要各自持有一份,所以创建进程需要的非常大的开销。

  进程数据共享

  由于进程在内存中具有独立的地址空间,且每个地址空间各持有一份数据,默认情况下无法共享数据  

#方法一:进程共享数据:Array
from multiprocessing import Process
from multiprocessing import Array
def foo(i,arg):
arg[i] = i +100 #每一个进程加100
for item in arg: #
print(item) #输出迭代数据
print("----------")
if __name__ == "__main__":
li = Array('i',10) #创建数组,i 等于数据格式为整型,并设置10个元素给数据
for i in range(10): #设置10个进程
p = Process(target=foo,args=(i,li,))#创建进程
p.start() #执行进程
#方法二:常用进程共享
from multiprocessing import Process
from multiprocessing import Manager
def foo(i,arg):
arg[i] = i +100
print(arg.values())
if __name__ == "__main__":
obj = Manager() #创建对象
li = obj.dict() #创建字典
for i in range(10):
p = Process(target=foo,args=(i,li,))
p.start()
# time.sleep(0.01) #方式一:
p.join() #方式二:join进程之间串联进行
    'c': ctypes.c_char,  'u': ctypes.c_wchar,
'b': ctypes.c_byte, 'B': ctypes.c_ubyte,
'h': ctypes.c_short, 'H': ctypes.c_ushort,
'i': ctypes.c_int, 'I': ctypes.c_uint,
'l': ctypes.c_long, 'L': ctypes.c_ulong,
'f': ctypes.c_float, 'd': ctypes.c_double

类型对应表

#创建进程池
from multiprocessing import Process,Queue
def f(i,q):
print(i,q.get()) #回去队列中元素 if __name__ == "__main__":
q=Queue() #创建队列
q.put(123) #将数据put进队列
q.put(456)
q.put(789)
for i in range(10): #共设置10个进程池
p = Process(target=f,args=(i,q,)) #创建进程
p.start() #执行进程

  进程池

  进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。

  进程池常用方法:  

  • apply  :串行执行每一个进程,即只有当一个进程执行完之后,第二个进程才会开始执行
  • apply_async  :并行执行所有进程,同时执行所有进程
from  multiprocessing import Pool
import time
def f1(arg):
time.sleep(1)
print(arg)
if __name__ == "__main__":
pool = Pool(5) #创建进程池
for i in range(10): #创建10个进程
pool.apply(func=f1,args=(i,)) #apply:让进程去进程池中获取数据,只有等待一个执行完一个元素后,第二个进程接着执行
# pool.apply_async(func=f1,args=(i,))
print("end") #当0个进程执行完之后,输出end
  • apply_async  :并行执行所有进程,同时执行所有进程
  • terminate()#程序执行此时,立即终止所有进程,不管进程池中是否含有任务
from  multiprocessing import Pool
import time
def f1(arg):
time.sleep(1)
print(arg)
if __name__ == "__main__":
pool = Pool(5) #创建进程池,每一次可同时执行5个进程
for i in range(10): #创建10个进程
#pool.apply(func=f1,args=(i,)) #apply:让进程去进程池中获取数据,只有等待一个执行完一个元素后,第二个进程接着执行
pool.apply_async(func=f1,args=(i,)) #apply_async:方法:并行运行所有进程
# print("end") #当0个进程执行完之后,输出end
pool.close()#所有任务(即)执行完毕
time.sleep(2)
pool.terminate()#当程序运行此时,立即终止所有进程,不管进程池中任务是否执行完
# pool.join()

  进程中常用方法由:Lock,Rloack,Event,Condition,Smaphore等于线程中方法一样

  1、进程锁:  

from multiprocessing import RLock,Lock,Event,Condition,Semaphore
from multiprocessing import Process
from multiprocessing import queues
from multiprocessing import Array
import multiprocessing
import time def foo(i,list,lc):
lc.acquire() #给进程上锁
list[0]=list[0]-1 #每一次进程调用子进程时减一
time.sleep(1)
print("say hi",list[0]) #输出每一次进程调用
lc.release() #给进程解锁
if __name__ == "__main__":
li = Array('i',1) #python引用数组,i:表示整型数据,1:带包进程数组中含有一个元素
 li[0]=10 #设置数组长度为10 lock = RLock() #创建进程锁 for i in range(10): #创建10个进程 p = Process(target=foo,args=(i,li,lock)) #创建进程 p.start() #执行进程

  2、多进程

二、Python线程

  定义:线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必  不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

  threading模块:提供线程相关操作,线程是应用程序中工作的最小单位

  1、单线程

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# lcj
import threading
import time
#创建一个class类
def show(arg): #arg:传参
time.sleep(2)
print('thread'+str(arg)) #将传参转换至字符串形式
for i in range(10): #创建10个线程
t = threading.Thread(target=show,args=(i,)) #创建线程
t.start() #线程就绪,等到CPU调度,
# print('main thread stop')

 上述代码创建了10个“前台”线程,然后控制器就交给了CPU,CPU根据指定算法进行调度,分片执行指令

 更多方法:
•start 线程准备就绪,等待CPU调度
•setName 为线程设置名称
•getName 获取线程名称
•setDaemon 设置为后台线程或前台线程(默认)
如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止
如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
•join 逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
•run 线程被cpu调度后自动执行线程对象的run方法  
#自定义线程
import threading
import time
class MyThread(threading.Thread): #继承父类threading.Thread
def __init__(self, num): #MyThread类__ini__方法
threading.Thread.__init__(self)
self.num = num
def run(self): #定义每个线程执行的函数,此时:self == 调用对象(t1或者t2对象)
print('running on number:%s' % self.num)
time.sleep(3) #每一个线程睡眠3秒
if __name__ == "__main__":
t1 = MyThread(1) #创建t1对象,并将参数1复制给t1对象
t2 = MyThread(2) #创建t2对象,并将参数1复制给t2对象
t1.start() #执行线程
t2.start()
#输出
# running on number:1
# running on number:2

自定义线程 

import threading
def f1(arg):
print(arg)
for i in range(10): #设置10个线程
t = threading.Thread(target=f1,args=(i,)) #创建线程
t.start() #执行线程
#输出:0-9数据

自定义线程二

 

  生产者和消费者

import queue,time
import threading
#先进先出
q = queue.Queue(20)
#生产者
def productor(arg):
"""
买票
:param arg:
:return:
"""
q.put(str(arg)+"-买票")
for i in range(30):
t = threading.Thread(target=productor,args=(i,)) #创建买票线程生产者,并将动态参数i传递给productor(arg)
t.start() #执行买票线程
# if i == 30:
# break
#消费者
def consumer(arg):
"""
服务器后台,
:param arg:
:return:
"""
while True:
print(arg,q.get()) #获取队列中存在元素
time.sleep(1)
sk = threading.BoundedSemaphore(5)
for j in range(5):
t = threading.Thread(target=consumer,args=(j,))#创建买票线程生产者,并将动态参数j传递给consumer(arg)
t.start() #执行c线程

生产者和消费者模式

    队列

  Python中常用队列方法:

  put放数据,是否阻塞,阻塞时的超时事件

  get取数据(默认阻塞),是否阻塞,阻塞时的超时事件

  qsize():获取队列中有多少个元素

  maxsize:最大支持的个数

  1、先进先出

   queue.Queue(2)方法:创建先进先出队列,参数2表示队列中最多可含有两个元素,否则超过队列中设置的最大队列则报队列满(queue.Full)错误, 

import queue
import time
#先进先出
# put放数据,是否阻塞,阻塞时的超时事件
# get取数据(默认阻塞),是否阻塞,阻塞时的超时事件
# qsize():获取队列中有多少个元素
# maxsize 最大支持的个数
q = queue.Queue(2) #创建队列,带参数,则表示队列最大可放多少个数据
q.put(12)
q.put(13)
print(q.qsize()) #获取队列中有多少个数据
# q.put(15,timeout=2) #当队列中设置长度为2时,此时在往队列中增加元素时,超过2秒,报错queue.Full
q.put(15,block=False,timeout=2)
print(q.qsize()) #获取队列中有多少个数据
print("------------")
print(q.get()) #获取队列中元素
print(q.get(block=False)) #get取数据(默认阻塞),当block=False表示不堵塞
print(q.get(timeout=2)) #设置获取队列中元素超时时间2秒,超过则直接执行下面代码
print(q.get(block=False,timeout=2)) #

  2、后进先出

  queue.LifoQueue()方法:创建后进先出队列

import queue
# 队列后进先出
q= queue.LifoQueue()
q.put(123) #将数据put队列中
q.put(234)
print(q.get()) #获取队列中的元素
#输出:234

  3、队列优先级

  queue.PriorityQueue()方法:创建队列优先级队列

import  queue
#队列优先级,按照下标进行优先级别排序
q = queue.PriorityQueue() #创建队列优先级别q值
q.put((1,"lcj")) #将数据put队列中
q.put((0,"alex"))
q.put((4,"xiaoluo"))
print(q.get()) #获取队列中的元素
#输出(0, 'alex')

  4、双向队列

  queue.deque()方法:创建双向队列

import queue
#双向队列
q = queue.deque() #创建双向队列
q.append(33) #将元素33加入队列中
q.append(44)
q.append(55)
q.appendleft(99)
print(q.pop()) #pop:在队列中提取一个元素
print(q.popleft()) #

  线程锁(Lock,RLock)

  一个进程下有多个线程,且线程是共享进程资源,每一个线程肯能执行N条语句后,当多个线程同时修改一天数据时可能会出现脏数据,所以,出现线程锁,同一时间只允许一个线程操作

  Lock,RLock区别?

  Lock是阻塞其他线程对共享资源的访问,且同一线程只能acquire一次,如多于一次就出现了死锁,程序无法继续执行。为了保证线程对共享资源的独占,又避免死锁的出现,就有了RLock。RLock允许在同一线程中被多次acquire,线程对共享资源的释放需要把所有锁都release。即n次acquire,需要n次release

  线程在未加锁情况下,主线程回一次性执行所有的线程且会产生多个线程同用一个数据,产生脏数据  

#未加锁
import threading
import time
num = 0 #定义一个全局变量
def show(arg):
global num #对全局变量num进行重新赋值
time.sleep(1)
num +=1
print(num)
for i in range(10): #定义十个线程
t = threading.Thread(target=show,args=(i,))#
t.start() # 执行线程

线程未加锁

线程加锁特定:每一个线程执行一条数据,防止多个线程重用一条数据产生脏数据

#加锁
import threading
import time
num = 0 #定义一个全局变量
lock = threading.RLock() #定义一个线程锁
def show(arg):
lock.acquire() #枷锁操作
global num #对全局变量num进行重新赋值
num +=1
time.sleep(1) #每一个线程执行时睡眠一秒钟
print(num)
lock.release() #解锁操作
for i in range(10): #定义十个线程
t = threading.Thread(target=show,args=(i,))#创建主线程
t.start() # 执行线程

线程加锁

  信号量(Semaphore)

  互拆锁:同时只允许一个线程更改数据,而Semapphore是同时允许一定数量的线程更改数据,比如:一个萝卜一个坑,只有空余的坑才能进入占座

  BoundedSemaphore(5)方法:表示5个线程同时运行

import threading,time
def run(n):
k1.acquire() #加锁
time.sleep(1)
print("run the threading:%s" %n)
k1.release() #解锁
if __name__ == '__main__':
num = 0
k1 = threading.BoundedSemaphore(5) #设置线程最多同时运行5个线程
for i in range(30): #设置线程数为30
t = threading.Thread(target=run,args=(i,)) #主线程
t.start() #执行线程

  事件(event)

  python线程的事件用于主线程控制其他子线程的执行,事件主要提供是三个方法:set、wait、clear

  事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。  

  • clear:将“Flag”设置为False
  • set:将“Flag”设置为True
  • wait:检测主线程状态
import threading,time
#代码从上往下执行,由t.start调用子线程执行lcj方法,第一次输出i值,
#当线程执行值wait时,判断线程状态,红灯(clear):停,绿灯(set):行
#主线程继续运行至inp,当用户输如true时,主线程执行set方法,将线程转态设置绿灯,继续运行,则子线程继续执行i+100
def lcj(i,event):
print(i)
time.sleep(1)
event.wait() #检测是什么等,如果是红灯,则停,绿灯:则行
print(i+100)
event_obj = threading.Event() #创建一个线程事件
for i in range(10): #设置30个线程
t = threading.Thread(target=lcj,args=(i,event_obj,))
t.start() #调用线程
event_obj.clear() #设置成红灯
inp = input(">>:").strip()
if inp =='true': #如果用户输入:true,系统则经过wait检查之后执行其下面打印语句
event_obj.set() #设置成绿灯

  条件(Contion)

  当线程处于等待时,只有满足某条件时,才释放N个线程继续执行子线程下代码

  1、条件wait方法用法

  notify方法:不会释放所占用的锁,输入几就释放几个元素,比如:客户输入2,则子线程输出100,101
import threading
def run(i,con):
print(i)
con.acquire() #加锁
con.wait() #检测主线程状态,当主线程客户端输入3,则子线程输出队列中前三个元素每一个各加100输出
print(i+100)
con.release() #解锁
# if __name__ == "__main__":
con = threading.Condition()
for i in range(10):
t = threading.Thread(target=run,args=(i,con,)) #创建主线程
t.start() #执行子线程
while True:
inp = input(">>>:").strip()
if inp == 'q':
break
# 此三个必须放在一起、acquire、notify、release
con.acquire() #加锁
con.notify(int(inp)) #notify()方法不会释放所占用的锁,输入几就释放几个元素,比如:客户输入2,则子线程输出100,101
con.release() #解锁

  2、条件wait_for方法:  

import  threading
def contion():
ret = False
i = input(">>>>:")
if i =="true":
ret = True
else:
ret = False
return ret
def run(i,con):
print(i) #输出所有线程
con.acquire()#加锁
con.wait_for(contion) #wait_for:等待条件=true成立,则执行contion函数中语句
print(i+100) #当主线程条件成立,则执行i+100
con.release() #解锁
c = threading.Condition() #创建线程条件
for i in range(10): #设置10个线程
t = threading.Thread(target=run,args=(i,c,)) #创建线程
t.start() #执行线程

  定时器(time)

  python中定时器:指定N秒之后执行子线程操作  

from threading import Timer
def lcj():
print("hello,world")
t = Timer(1,lcj) #定时器:1秒后执行lcj函数,并输出hello,world
t.start() #执行线程

  2、多线程

三、线程池

  线程池原理:可理解为是一个容器(容器中可指定大小),在容器中取一个线程则少一个线程,再无没有线程时等待,线程执行完毕,将线程归还给线程池

  1、基本线程池

#自定义线程池
import queue
import threading
import time
class ThreadPool: #进程池类
def __init__(self,maxsize=5): #创建构造方法,设置最大线程为5个线程
self.maxsize = maxsize
self._q = queue.Queue(maxsize)#_q:下划线 为了区别,创建队列
for i in range(maxsize): #每次循环最大线程
self._q.put(threading.Thread) #将类名put队列中
def get_thread(self):
return self._q.get() #获取线程
def add_thread(self):
self._q.put(threading.Thread)#队列含有:threading.Thread
pool = ThreadPool(5) #线程池最大个数为5,即每次最多只能获取5个线程
def task(arg,p):
print(arg)
time.sleep(1) #
p.add_thread()#从线程池取完一次数据之后 再次想线程池中获取数据,直到执行完100个线程
for i in range(100): #循环100次
#t==threading.Thread类
t = pool.get_thread()
obj = t(target=task,args=(i,pool,)) #创建主线程
obj.start() #执行子线程池

  2、高级线程池

四、Python协程

  协程原理:利用一个线程,分解一个线程成为多个“微线程”

  线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。

  协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

  协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;

  协程常用方法

  greenlet:底层  

from greenlet import greenlet
def f1():
print(12)
lcj2.switch()
print(34)
lcj2.switch()
def f2():
print(56)
lcj1.switch()
print(78)
lcj1 = greenlet(f1)
lcj2 = greenlet(f2)
lcj1.switch()
#第一步:lcj1.switch():执行f1函数并输出12
#第二步:在函数f1中lcj2.switch(),则执行f2函数并输出56
#第三步:在f2函数执行lcj1.switch(),则执行f1函数,并输出34
#第四步:在f1函数执行lcj2.switch(),则执行f2函数,并输出78
#最后输出顺序为:12,56,34,78

  gevent:高级

import gevent
def foo():
print("running in foo")
gevent.sleep(1)
print('Explicit context switch to foo agein') def bar():
print('explicit context to bar')
gevent.sleep(1)
print('implicit context switch back to bar')
gevent.joinall([
gevent.spawn(foo),
gevent.spawn(bar),
])
# running in foo
# explicit context to bar
# Explicit context switch to foo agein
# implicit context switch back to bar

  遇到IO操作自动切换:  

from gevent import monkey;monkey.patch_all()
#monkey.patch_all() 修改原来socket, 对IO请求进行封装
import gevent
import requests
def f(url):
print('GET: %s'% url) #获取所有URL
resp = requests.get(url) #获取URL
data = resp.text #获取URL内容
print("%d bytes received from %s." % (len(data),url))
gevent.joinall([
gevent.spawn(f, "https://www.python.org/"),
gevent.spawn(f, "https://www.yahoo.com/"),
gevent.spawn(f, "https://github.com/"),
])

Python_进程、线程及协程的更多相关文章

  1. python进程.线程和协程的总结

    I.进程: II.多线程threading总结 threading用于提供线程相关的操作,线程是应用系统中工作的最小单位(cpu调用的最小单位). Python当前版本的多线程没有实现优先级,线程组, ...

  2. python系列7进程线程和协程

    目录 进程 线程 协程  上下文切换 前言:线程和进程的关系图 由下图可知,在每个应用程序执行的过程中,都会去产生一个主进程和主线程来完成工作,当我们需要并发的执行的时候,就会通过主进程去生成一系列的 ...

  3. Python_多任务:进程、线程、协程

    进程 进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体.进程是一种抽象的概念,从来没有统一的标准定义.进程一般由程序 ...

  4. Python(八)进程、线程、协程篇

    本章内容: 线程(线程锁.threading.Event.queue 队列.生产者消费者模型.自定义线程池) 进程(数据共享.进程池) 协程 线程 Threading用于提供线程相关的操作.线程是应用 ...

  5. Python学习之路--进程,线程,协程

    进程.与线程区别 cpu运行原理 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者消费者模型 Q ...

  6. Python—进程、线程、协程

    一.线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务 方法: ...

  7. Queue、进程、线程、协程

    参考博客地址 http://www.cnblogs.com/alex3714/articles/5230609.html 1.python GIL全局解释器锁 python调用的操作系统的原生线程,当 ...

  8. 11.python之线程,协程,进程,

    一,进程与线程 1.什么是线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行 ...

  9. python中socket、进程、线程、协程、池的创建方式和应用场景

    进程 场景 利用多核.高计算型的程序.启动数量有限 进程是计算机中最小的资源分配单位 进程和线程是包含关系 每个进程中都至少有一条线程 可以利用多核,数据隔离 创建 销毁 切换 时间开销都比较大 随着 ...

  10. python 进程、线程与协程的区别

    进程.线程与协程区别总结 - 1.进程是计算器最小资源分配单位 - 2.线程是CPU调度的最小单位 - 3.进程切换需要的资源很最大,效率很低 - 4.线程切换需要的资源一般,效率一般(当然了在不考虑 ...

随机推荐

  1. php大力力 [027节] 被百度收录较好的几个视频网站示例

    php大力力 [027节] 被百度收录较好的几个视频网站示例 56网 很清晰 :2014 兄弟连高洛峰 PHP教程14.1.7 在PHP脚本中操作MySQL数据库4_视频在线观看 - 56.com 土 ...

  2. iOS:开发者中心证书创建流程

    一,首先点击开发者首页(https://developer.apple.com/)里面的Member Center.二,输入开发者账号和密码,点击sign in登录.三,点击Certificates, ...

  3. 数据库 SQL优化

    1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...

  4. /bin/bash^M: bad interpreter: 没有那个文件或目录

  5. HDU 1001 Sum Problem(AC代码)

    #include <stdio.h> int main(){ int k,sum; while(scanf("%d",&k)!=EOF){ ==){ sum=( ...

  6. Mapnik 教程

    mapnik::parameters 参数解析 mapnik::parameters params; params["encoding"] = "utf-8" ...

  7. 【转】7 Tips to Speed Up Eclipse

    技巧一:运行最新版本的JDK和Eclipse 通常,新版本的JDK和Eclipse都会有性能上的优化.请确保你使用的是64位Eclipse并且使用了Oracle的JDK.对于网络开发,需要使用Ecli ...

  8. 服务调用方案(Spring Http Invoker) - 我们到底能走多远系列(40)

    我们到底能走多远系列(40) 扯淡:  判断是否加可以效力于这家公司,一个很好的判断是,接触下这公司工作几年的员工,了解下生活工作状态,这就是你几年后的状态,如果满意就可以考虑加入了. 主题: 场景: ...

  9. htseq-count 的使用

    Given a file with aligned sequencing reads and a list of genomic features, a common task is to count ...

  10. myeclipse 清理项目缓存的几大步骤

    http://blog.csdn.net/moneyshi/article/details/49247169 相信大家被项目缓存折腾过吧,这里罗列几条清除项目缓存的方法 1.项目清理: 选择菜单栏的P ...