python之day11(线程,进程,协程,memcache,redis)
一 线程
基本使用
import threading
def f1(arg):
print(arg)
t = threading.Thread(target=f1,args=("hello",)) #target 调用的 函数名 ,args 写入的参数
t.start() #开始等待线程调用
自定义线程类:
import threading
class MyThread(threading.Thread): #建立类,继承threading.Thread
def __init__(self, func, args):
self.func = func
self.args = args
super(MyThread, self).__init__() #使用父类的__init__函数
def run(self):
self.func(self.args)
def f2(arg):
print(arg)
obj = MyThread(f2,123) #类似于t = threading.Thread(target=f2,args=(123,))
obj.start()
线程锁
基本线程锁使用:
import threading
import time
NUM = 10
def func(l):
global NUM
#上锁
l.acquire()
NUM -= 1
time.sleep(2)
print(NUM)
#开锁
l.release()
lock = threading.RLock() 创建对象 锁
for i in range(10):
t = threading.Thread(target=func, args = (lock, ))
t.start()
#一个数字一个数字的 顺序打印 中间间隔1秒
自控线程锁:
import threading
def condition():
ret = False
r = input('>>>')
if r == 'true':
ret = True
else:
ret = False
return ret
def func(i,con):
#print(i) #先打印一边i
con.acquire() #锁定
con.wait_for(condition) #如果返回true
print(i+100) #打印i+100
con.release() #解锁
c = threading.Condition()
for i in range(10):
t = threading.Thread(target=func, args=(i,c,))
t.start()
事件型线程锁:
import threading
def func(i,e):
print(i)
e.wait() #检测是否是什么灯 默认是红灯 所以现在是红灯
print(i+100) #绿灯就执行i+100 是绿灯之后直接执行+100
event = threading.Event()
for i in range(10):
t =threading.Thread(target=func, args=(i, event,))
t.start()
event.clear() #设置成红灯
inp = input(">>>")
if inp == "1":
event.set() #设置成绿灯
threading.BoundedSemaphore(5)
锁住线程的数量
import threading
import time
NUM = 10
def func(i, l):
global NUM
l.acquire()
NUM -= 1
time.sleep(2)
print(NUM)
#开锁
l.release()
lock = threading.BoundedSemaphore(5) #锁5个线程
for i in range(10):
t = threading.Thread(target=func, args = (i, lock, ))
t.start()
threading.Condition()
全部锁住,然后根据数量慢慢解锁:
import threading
def func(i,con):
print(i)
con.acquire()
con.wait()
print(i+100)
con.release()
c = threading.Condition()
for i in range(10):
t = threading.Thread(target=func, args=(i, c, ))
t.start()
while True:
inp = input(">>>")
if inp == "q":
break
c.acquire()
c.notify(int(inp)) #如果数量是1 就出现0+100 是2 就是打印出 1+100 和2+100 两个,知道都执行完成
c.release()
通过时间来锁线程:
timer:
from threading import Timer
def hello():
print("hello world")
t = Timer(0.1,hello) #0.1 是多长时间解锁
t.start()
PS:threading.RLock和threading.Lock的区别
1 在threading模块中,定义两种类型的锁:threading.Lock和threading.RLock。它们之间有一点细微的区别,通过比较下面两段代码来说明: 2 3 import threading 4 lock = threading.Lock() #Lock对象 5 lock.acquire() 6 lock.acquire() #产生了死锁。 7 lock.release() 8 lock.release() 9 10 import threading 11 rLock = threading.RLock() #RLock对象 12 rLock.acquire() 13 rLock.acquire() #在同一线程内,程序不会堵塞。 14 rLock.release() 15 rLock.release() 16 17 18 这两种琐的主要区别是:RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。注意:如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的锁。threading.Condition 19 20 可以把Condiftion理解为一把高级的锁,它提供了比Lock, RLock更高级的功能,允许我们能够控制复杂的线程同步问题。threadiong.Condition在内部维护一个锁对象(默认是RLock),可以在创建Condigtion对象的时候把锁对象作为参数传入。Condition也提供了acquire, release方法,其含义与锁的acquire, release方法一致,其实它只是简单的调用内部锁对象的对应的方法而已。Condition还提供了如下方法(特别要注意:这些方法只有在占用锁(acquire)之后才能调用,否则将会报RuntimeError异常。): 21 Condition.wait([timeout]): 22 23 wait方法释放内部所占用的锁,同时线程被挂起,直至接收到通知被唤醒或超时(如果提供了timeout参数的话)。当线程被唤醒并重新占有锁的时候,程序才会继续执行下去。 24 Condition.notify(): 25 26 唤醒一个挂起的线程(如果存在挂起的线程)。注意:notify()方法不会释放所占用的锁。 27 Condition.notify_all() 28 Condition.notifyAll() 29 30 唤醒所有挂起的线程(如果存在挂起的线程)。注意:这些方法不会释放所占用的锁。
**自定义线程池
import queue
import threading
import time
class ThreadPool():
def __init__(self, maxclient = 5 ):
self.maxclient = maxclient
self._q = queue.Queue(maxclient) #建立队列
for i in range(maxclient):
self._q.put(threading.Thread) #往队列中放一个线程
#[threading.Thread,threading.Thread,threading.Thread,threading.Thread,threading.Thread,threading.Thread]
def get_thread(self):
return self._q.get() #取出队列中的一个数据(数据就是一个线程),返回threading.Thread这个类名
def add_thread(self):
self._q.put(threading.Thread) #再将一个数据加入队列(数据就是一个线程)
pool = ThreadPool(5) #创建对象
def task(arg, p):
print(arg)
time.sleep(1)
p.add_thread() #执行完一个线程,再将令牌还回队列中,就在队列中再加一个线程,可以写成pool.add_thread()
for i in range(100): #一共100个任务
t = pool.get_thread() # 创建一个线程
obj = t(target=task, args=(i, pool)) #规定好要执行的函数和参数
obj.start() #开始执行,**一定不能忘
先进先出队列:
import queue
q = queue.Queue(10)
# 队列的最大长度是10
q.put(11)
#put放数据,是否阻塞,阻塞时的超时时间
q.put(22)
q.put(33, block = False, timeout = 2)
# 33是数据,2是超时时间,
#2秒钟后 如果有位置可以加入队列
# block = False 不阻塞。
print(q.qsize())
#qsize() 真实个数
#maxsize() 最大个数
#join, task_done, 阻塞进程,当队列中任务执行完毕之后,不再阻塞
print(q.get())
#get是取数据,默认阻塞 阻塞时的超时时间
import queue q = queue.Queue(2) #限制为2个数据的队列 print(q.empty()) #打印队列中是否插入数据了。返回True 或者False q.put(111) #插入 111 q.put(222) #插入222 print(q.qsize()) #显示有几个数据 2个 q.get() #获取第一个数据 q.task_done() #获取完毕 q.get() #获取第二个数据 q.task_done() #获取完毕 print(q.qsize()) #再看看队列中的数据个数 q.join() 主线程等待子线程执行完毕在关闭
其他队列:
import queue q = queue.LifoQueue() #last in front out = Lifo 后进先出 q.put(123) q.put(234) print(q.get()) q = queue.PriorityQueue() # 1 0 6 是权重,数字越小优先级越高 如果权重相同就按照先后顺序 q.put((1,"1")) q.put((0,"2")) q.put((6,"3")) print(q.get()) #因此先出 “2” q = queue.deque() #双向进出队列 q.append(123) #默认往右加数据 q.append(456) q.appendleft(100) #特定从左加 排列就是 456 123 100 print(q.pop()) #取第一个数据 456 print(q.popleft()) #向左取数据 100
生产者消费者模型(队列)
做包子
import queue
import threading
import time
q = queue.Queue(20) #队列最大是20个数据
def productor(arg):
'''
生产者
:param arg:
:return:
'''
while True: #循环做包子
q.put(str(arg) + "-包子") #做出一个包子
time.sleep(1) #耗时1秒钟
def consumer(arg): #买包子的
while True: #排队买包子
print(arg , q.get()) #打印出arg第记号顾客,q.get() 打印出队列中的数据(厨师标号 + "-包子")
time.sleep(1) #1秒钟吃完再去排队
for i in range(3): #3个厨师做包子
t = threading.Thread(target = productor, args = (i, ))
t.start()
for j in range(20): #20个人排队买包子
t = threading.Thread(target = consumer, args = (j, ))
t.start()
二 进程
基本使用
进程之间是不能互相通讯的
from multiprocessing import Process
import threading
import time
def foo(i):
print('say hi',i)
if __name__ == "__main__": #windows 必须增加这行 linux 不需要
for i in range(10):
p = Process(target=foo,args=(i,))
p.start()
进程锁
进程数据共享
默认数据不共享
*queues
from multiprocessing import Process
from multiprocessing import queues
import multiprocessing
def foo(i, arg):
arg.put(i)
print("say hi ", i, arg.qsize())
if __name__ == "__main__":
li = queues.Queue(20, ctx=multiprocessing) #完成进程间的传递信息
for i in range(10):
p = Process(target=foo, args=(i, li))
p.start()
p.join()
*array 数组格式:
from multiprocessing import Process
# from multiprocessing import queues
import multiprocessing
from multiprocessing import Array #数组
def foo(i, arg):
arg[i] = i + 100
# print(arg)
for item in arg:
print(item)
print("============")
if __name__ == "__main__":
li = Array("i", 10) #i 是数 int 只能是“i” 数组中只能是数字。
for i in range(10):
p = Process(target=foo, args=(i, li,))
p.start()
'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
*Manager.dict
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()
import time
time.sleep(1.1)
pipe
进程池
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(30):
# pool.apply(func=f1, args=(i,)) #串行进行 1个1个执行
pool.apply_async(func=f1,args=(i,)) #异步进行 ,5个5个执行
pool.close() #所有的任务执行完毕
# time.sleep(1)
# pool.terminate() #立即终止
pool.join()
pool.close() #所有的任务执行完毕
pool.terminate() #立即终止
PS:
IO 密集型--多线程
计算密集型-- 多进程
三 协程
原理:利用一个线程,分解一个线程成为多个”微线程“
greenlet
gevent
封装了greenlet
pip3 install gevent
from gevent import monkey; monkey.patch_all()
import gevent
import requests
def f(url):
print("GET: %s" % url)
resp = requests.get(url) #获取url的信息
data = resp.text
print("%d bytes received from %s" % (len(data), url))
gevent.joinall([
gevent.spawn(f, "https://www.python.org/"), #是一个列表的形式,f是函数名,后面跟函数的参数
gevent.spawn(f, "https://www.yahoo.com/"),
gevent.spawn(f, "https://github.com/"),
])
四 缓存
1,安装软件
2,程序:安装其对应的模块
Socket 连接
memcache
1 天生集群
2 基本
3 gets,cas
k -> ""
基本使用方法(通过key获取值)
import memcache
mc = memcache.Client(['192.168.61.128:12000'], debug=True) #连接远程memcache服务端
mc.set("foo", "bar") #设置key 和value 值
ret = mc.get('foo') #获取
print(ret) #打印出 value值
天生支持集群模式:
import memcache
mc = memcache.Client([('192.168.61.128:12000', 1), ('192.168.61.128:12001', 2), ('192.168.61.128:12002', 1)], debug=True)
#每一个服务端,通过元组显示,前面是IP和端口,后面是权重。
# 权重是1 该服务器就出现一次,2就出现两次 现实就是['192.168.61.128:12000','192.168.61.128:12001','192.168.61.128:12001','192.168.61.128:12002']
mc.set('k1', 'v1')
#根据算法将 k1 转换成一个数字
#将数字和主机列表长度求余数,得到一个值 N( 0 <= N < 列表长度 )
#在主机列表中根据 第2步得到的值为索引获取主机,例如:host_list[N]
#连接 将第3步中获取的主机,将 k1 = "v1" 放置在该服务器的内存中
add
添加相同key的值会报错:
mc = memcache.Client(['192.168.61.128:12000'], debug=True)
mc.add('k1', 'v2')
# mc.add('k1', 'v2') # 报错,对已经存在的key重复添加,失败!!!
#MemCached: while expecting 'STORED', got unexpected response 'NOT_STORED'
replace
替换不存在的key值会报错:
import memcache
mc = memcache.Client(['192.168.61.128:12000'], debug=True)
# 如果memcache中存在kkkk,则替换成功,否则一场
mc.replace('k2','999')
#k2不存在
#MemCached: while expecting 'STORED', got unexpected response 'NOT_STORED'
set 和 set_multi
set 设置一个键值对,如果key不存在,则创建,如果key存在,则修改
set_multi 设置多个键值对,如果key不存在,则创建,如果key存在,则修改
import memcache
mc = memcache.Client(['192.168.61.128:12000'], debug=True)
mc.set('key0', 'wupeiqi')
mc.set_multi({'key1': 'val1', 'key2': 'val2'})
print(mc.get("key0"))
print(mc.get("key1"))
print(mc.get("key2"))
delete 和 delete_multi
delete 在Memcached中删除指定的一个键值对
delete_multi 在Memcached中删除指定的多个键值对
import memcache
mc = memcache.Client(['192.168.61.128:12000'], debug=True)
mc.delete('key0')
mc.delete_multi(['key1', 'key2']) #删除 是列表格式
print(mc.get("key0"))
print(mc.get("key1"))
print(mc.get("key2"))
#回复3个none
get 和 get_multi
get 获取一个键值对
get_multi 获取多一个键值对
import memcache
mc = memcache.Client(['192.168.61.128:12000'], debug=True)
mc.set('key0', 'wupeiqi')
mc.set_multi({'key1': 'val1', 'key2': 'val2'}) #设置是字典来对应key和value
val = mc.get('key0')
item_dict = mc.get_multi(["key1", "key2", "key3"])
print(val,item_dict)
##wupeiqi {'key2': 'val2', 'key1': 'val1'} key3 不存在就没有
append 和 prepend
append 修改指定key的值,在该值 后面 追加内容
prepend 修改指定key的值,在该值 前面 插入内容
import memcache
mc = memcache.Client(['192.168.61.128:12000'], debug=True)
# k1 = "v1"
mc.append('k1', 'after')
# k1 = "v1after"
mc.prepend('k1', 'before')
# k1 = "beforev1after"
print(mc.get("k1"))
decr 和 incr
incr 自增,将Memcached中的某一个值增加 N ( N默认为1 )
decr 自减,将Memcached中的某一个值减少 N ( N默认为1 )
import memcache
mc = memcache.Client(['192.168.61.128:12000'], debug=True)
mc.set('k1', '777')
mc.incr('k1')
# k1 = 778
print(mc.get('k1'))
mc.incr('k1', 10)
print(mc.get('k1'))
# k1 = 788
mc.decr('k1')
print(mc.get('k1'))
# k1 = 787
mc.decr('k1', 10)
print(mc.get('k1'))
# k1 = 777
gets 和 cas
避免脏数据,有两个客户同时更改同一个key的value 导致数据不准确。
解决办法:每次执行gets时,会从memcache中获取一个自增的数字,通过cas去修改gets的值时,会携带之前获取的自增值和memcache中的自增值 进行比较,如果相等,则可以提交,如果不想等,那表示在gets和cas执行之间,又有其他人执行了gets(获取了缓冲的指定值), 如此一来有可能出 现非正常数据,则不允许修改。
import memcache
mc = memcache.Client(['192.168.61.128:12000'], debug=True, cache_cas=True)
v = mc.gets('product_count')
# ...
# 如果有人在gets之后和cas之前修改了product_count,那么,下面的设置将会执行失败,剖出异常,从而避免非正常数据的产生
mc.cas('product_count', "899")
redis
k - >""
k-> [11,222,33]
k -> {"k1":xxx}
k -> {11,22}
k-> [(11,1),(13,2)]
基本使用方法:
import redis
r = redis.Redis(host='192.168.61.128', port=6379)
r.set('foo', 'Bar')
print(r.get('foo'))
#b'Bar'
连接池
redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连 接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池。
import redis
pool = redis.ConnectionPool(host='192.168.61.128', port=6379)
r = redis.Redis(connection_pool=pool)
r.set('foo', 'Bar')
print(r.get('foo'))

浙公网安备 33010602011771号