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'))