本节内容:
线程:
a:基本的使用:
创建线程:
1:方法
1 import threading
2
3 def f1(x):
4 print(x)
5
6
7 if __name__=='__main__':
8 t=threading.Thread(target=f1,args=(1,))
9 t.start()
target=f1,args=(1,))创建线程,target=动作(执行什么,需要是可被调用的函数)args参数元组,要求参数最后加个逗号。
当我们创建一个线程时候,t.start()之后,线程已经准备就绪等待cpu调度。
cpu通过触发threading.Thread类中的run方法来调用我们传入的target 函数。通过debug断点调试可以看到如下:
在看下run方法。
所以我们可以自定义咱们自己的run方法,来被cpu调度。
2:方法
1 import threading
2
3
4 class myclass(threading.Thread):
5
6 def __init__(self,x):
7 super().__init__()
8 self.x=x
9
10 def run(self):
11 print(self.x)
12
13
14
15
16 if __name__=='__main__':
17 obj=myclass(1)
18 obj.start()
通过我们自定义run方法来覆盖父类中的run方法。
并通过start方法 来让cpu调度我们的线程obj。
如上2个方法的本质是相同的。都是cpu的调度run方法实现。
对比1和2方法,1方法是我们常用的。不推荐第二种方法。
b:队列 queue
1 import queue
2
3 q=queue.Queue()##先进先出的队列
4 q.put(11)#往队列里放数据。
5 q.put(12)
6 q.put(13)
7 print(q.qsize())#队列的长度
8 print(q.get())#队列取数据。
9 print(q.qsize())
10
11 3
12 11
13 2
1 import queue
2
3 q=queue.Queue(2)##先进先出的队列,可以设置队列的长度,默认是没限制。
4 q.put(11)#往队列里放数据。
5 q.put(12)
6 print(q.qsize())
7 q.put(13,timeout=2)#当队列的数据满了之后,在往里放数据会阻塞,等待队列长度改变。可以设置超时间。超时时间之后,队列的长度没改变,会报错。
8 print(q.qsize())#队列的长度
9 print(q.get())#队列取数据。
10 print(q.qsize())
11 2
12 Traceback (most recent call last):
13 File "C:/Users/Administrator/PycharmProjects/S12/day11/s3.py", line 7, in <module>
14 q.put(13,timeout=2)#当队列的数据满了之后,在往里放数据会阻塞,等待队列长度改变。可以设置超时间。
15 File "E:\python3.6\lib\queue.py", line 141, in put
16 raise Full
17 queue.Full
也可以设置放数据不阻塞。参数问block=False 默认该参数是True
1 import queue
2
3 q=queue.Queue(2)##先进先出的队列,可以设置队列的长度,默认是没限制。
4 q.put(11)#往队列里放数据。
5 q.put(12)
6 print(q.qsize())
7 q.put(111,block=False)##设置队列不阻塞。
8 print(q.qsize())#队列的长度
get同样也有这些方法。超时、是否阻塞。
1 import queue
2
3 q=queue.Queue(2)##先进先出的队列,可以设置队列的长度,默认是没限制。
4 q.put(11)#往队列里放数据。
5 q.put(12)
6 print(q.get())
7 print(q.get())#队列取数据。如果队列为空的时候获取数据的话,会阻塞。和get方法一样也可以设置超时间、是否阻塞。
8 print(q.get(timeout=2))
9 print(q.qsize())
10 11
11 12
12 Traceback (most recent call last):
13 File "C:/Users/Administrator/PycharmProjects/S12/day11/s4.py", line 8, in <module>
14 print(q.get(timeout=2))
15 File "E:\python3.6\lib\queue.py", line 172, in get
16 raise Empty
17 queue.Empty
1 import queue
2
3 q=queue.Queue(2)##先进先出的队列,可以设置队列的长度,默认是没限制。
4 q.put(11)#往队列里放数据。
5 q.put(12)
6 print(q.get())
7 print(q.get())#队列取数据。如果队列为空的时候获取数据的话,会阻塞。和get方法一样也可以设置超时间、是否阻塞。
8 print(q.get(block=False))
9 print(q.qsize())
其他方法:join和task_done组合使用。
1 import queue
2 q=queue.Queue(2)
3 q.put(1)
4 q.task_done()# 需要在每次取数据的时候告诉队列,这个数据已经完成了。是每次。这样join就不会阻塞了。
5 q.put(2)
6 q.task_done()
7 q.join()##当队列中有任务的时候,没做处理。该方法会阻塞,知道队列里的任务完成。可以配合task_done方法使用
需要注意:每次取数据需要使用task_done方法来告诉队列这个数据已经完成。
qsize,查看当前队列的长度。
get_nowait()实际上调用get(bliock=Flase)
put_nowait()也是调用put(block=Flase)
full()该方法表示检查当前队列是否满,返回是布尔值。
empty()该方法检查当前队列是否空,返回是布尔值。
1 import queue
2 q=queue.Queue(2)#构造方法中maxsize表示队列最大的长度。
3 q.put(11)
4 q.put(12)
5 print(q.qsize())## 查看当前队列长度。
6 print(q.empty())
7 print(q.full())
8 2
9 False
10 True
如上的队列是进程在内存里的队列,在python退出的时候队列也清空。
其他队列:如下队列都是queue的基础上操作的,继承queue这个类(除双向队列)。
先进后出队列:
1 import queue
2
3 q=queue.LifoQueue()##先进后出队列。first in last out
4 q.put(11)
5 q.put(2)
6 q.put(3)
7 print(q.get())
优先级队列,包含2个参数一个数据,一个是权重值。
1 import queue
2 q=queue.PriorityQueue()##权重优先队列,权重值越大,优先级越低。如果权重值一样,取第一个值。
3 q.put((11,5))
4 q.put((11,3))
5 q.put((12,3))
6 q.put((11,4))
7 print(q.get())
8 (11, 3)
双向队列:可以在队列的两边进行数据的输入和输出。
1 import queue
2 q=queue.deque()#双向队列,可以在2边进行取数据和放数据。很多方法和列表类似。
3 q.append(11)
4 q.appendleft(12)
5 print(q.pop())
6 print(q.popleft())
c:消费者和生产者模型:
主要解决了什么?
1:解决程序的阻塞问题。变相提高程序的处理效率和并发。
2:降低程序之间的解耦性。在修改任意一方的程序的时候,减少彼此的影响。
通过什么来实现?
可以通过消息队列,来实现中间的转换过程。
比如一个网站,在客户端同时有大并发的请求时候,可以通过应用程序来获取客户端的请求,并把请求放在消息队列中(这个操作很短,不容易出现阻塞情况。),此时客户端的请求,断开,应用程序把客户端的自动转发到另一个查询页面,这个页面
自动刷新,客户端请求的结果。后端程序在从消息队列中获取客户端的请求,进行处理,并更新数据库信息。当数据库更新成功之后,客户端查询就会显示结果。
好处:
1:降低了程序之间的耦合。
2:一个web请求,到达服务器之后,有后台程序进行处理。如果处理的时间过长,那么这个进程会被挂起,应用服务器会维护这个进程。如果大并发的请求,那么服务端会挂起较多进程,那么服务器会较多的资源浪费,而服务器也会出现hang死或者无效的请求,
因为一个sokcet维护的等待队列是有限,nginx的最大连接也是有限的。而这个模型很好的解决这个问题,他把客户端请求和后台程序处理请求分开。如果客户端请求过多,消息积压,后端可以提高服务器的数量来提高并发,而且这个后台应用程序不会维护客户端连接,
后台程序直接进行有效的请求处理,然后更新数据库,用户只需要查询数据库来验证自己的请求。
1 import queue
2 import threading
3 import time
4 q=queue.Queue(20)
5 def productor(j):
6 while True:
7 print('the server %s deal request'%j,q.get())#队列获取消息。
8 time.sleep(4)
9
10 def consumer(i):
11 q.put(' client NO:%s request'%i)#往队列中放消息。
12
13
14 for i in range(3):#后台3个程序在处理这个请求。
15 t=threading.Thread(target=productor,args=(i,))
16 t.start()
17 for j in range(40):
18 t=threading.Thread(target=consumer,args=(j,))##40个客户端请求。
19 t.start()
20
21
22 the server 0 deal request client NO:0 request
23 the server 1 deal request client NO:1 request
24 the server 2 deal request client NO:2 request
25 the server 1 deal request client NO:3 request
26 the server 0 deal request client NO:4 request
27 the server 2 deal request client NO:5 request
28 ......
d:线程锁:
线程是程序运行的最小单位,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流最小单元。一个标准线程是由线程ID,当前指令指针(pc),寄存器集合和堆栈组合,另外线程是进程的一个实体,是被系统独立调度和分派的基本单位。
线程不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程可以并发执行。由于线程之间的互相制约,致使线程在运行中呈现出间断性。
线程也有就绪、阻塞和运行的三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,等待处理。运行状态是指线程占有cpu正在运行,阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每个程序至少有一个线程,称为主线程。
在单个程序中同时运行多个线程完成不同工作,称为多线程。
基于线程和他的同一个进程所属的其他的线程共享资源,所以会出现以下情况:在多个线程共同对同一个数据进行修改的时候,会出现数据的不一致。
1 import threading
2 import time
3 x=10
4 def f1():
5
6 global x
7 x -= 1
8 time.sleep(2)
9 print(x)
10
11 for i in range(10):
12 t=threading.Thread(target=f1)
13 t.start()
14
15 0
16 0
17 0
18 .......
a:如上所示当多个线程对同一份数据进行修改的话,会出现数据混乱,并不是我们想要的数据。
避免多个线程对同一份数据同一时间进行修改,出现了互斥锁。
互斥锁:当一个线程在对数据进行修改的时候,如果加上互斥锁,只有当前线程执行的“动作”结束之后,下一个线程才可以进行相应的动作。
如下是只允许一个线程加锁和解锁。
1 import threading
2 import time
3 x=10
4 q=threading.RLock()
5 def f1():
6
7 global x
8 q.acquire()#加锁。递归锁
9 x -= 1
10 time.sleep(2)
11 q.release()#解锁。
12
13 print(x)
14
15 for i in range(10):
16 t=threading.Thread(target=f1)
17 t.start()
18 9
19 8
20 7
21 6
22 5
23 4
24 3
25 2
26 1
27 0
互斥锁有一次性锁和递归锁(可以锁多次)。上面用的是递归锁,可以锁多层。我们一般用递归锁。如下是一次性锁。
1 import threading
2 import time
3 x=10
4 q=threading.Lock()#一次性锁。
5 def f1(l):
6
7 global x
8 l.acquire()#上锁。
9 x -= 1
10 time.sleep(2)
11 l.release()#开锁。
12
13 print(x)
14
15 for i in range(10):
16 t=threading.Thread(target=f1,args=(q,))
17 t.start()
b:信号量:同一时间允许多个线程执行同一个动作。而不是上面的只允许单个线程同一时间执行同一个动作。
1 import threading
2 import time
3 x=10
4 q=threading.Semaphore(3)# 信号量,同一时间允许多少个线程执行相应的动作。
5 def f1(l,i):
6
7 global x
8 l.acquire()#上锁。
9 x -= 1
10 time.sleep(2)
11 l.release()#开锁。
12
13 print(x,'NO %s thread'%i)
14
15 for i in range(10):
16 t=threading.Thread(target=f1,args=(q,i))
17 t.start()
18 7 NO 0 thread
19 7 NO 2 thread
20 7 NO 1 thread
21 4 NO 4 thread
22 4 NO 3 thread
23 4 NO 5 thread
24 1 NO 6 thread
25 1 NO 8 thread
26 1 NO 7 thread
27 0 NO 9 thread
c:EVENT(事件):该事件是针对全部线程的动作,不是上面的单个或者多个。类似于红绿灯。红灯都不放行、绿灯都放行。
python线程的事件用于主线程控制其他线程执行,事件主要提供了三个方法:set wait clear。
事件处理机制:实际上python内部维护了一个全局的"flag",如果flag值为Flase(红灯)(默认是Flase),当执行到even.wait()时候,线程会阻塞。反之如果为True(绿灯)时候,event.wait()方法不会阻塞。
event.wait():检查全局flag值。如果是False,阻塞全部线程。反之放行。
event.clear()主动设置成全局变量为False.默认 值是False
event.set()主动设置全局变量为True。
1 import threading
2 import time
3 x=10
4 e=threading.Event()# 事件,同一时间阻塞所有线程或者同一时间放行所有线程。
5 def f1(l,i):
6
7 global x
8 l.wait()#检查全局flag值。
9 x -= 1
10
11
12 print(x,'NO %s thread'%i)
13
14 for i in range(10):
15 t=threading.Thread(target=f1,args=(e,i))
16 t.start()
17 e.clear()#设置全局标志:flag=Flase 默认值是False。
18 if __name__=='__main__':
19 inp=input(">")
20 if inp=='1':
21 e.set()##将全局标志:flag设置为True
d:Contidition 当满足一定条件时候,放行几个线程。
import threading
con=threading.Condition()#初始化一个Condition实例。
def func(c,i):
print(i)
c.acquire()
c.wait()
print(i+100)
c.release()
for i in range(10): t=threading.Thread(target=func,args=(con,i)) t.start() if __name__=="__main__": while True: inp=input('>>') if inp=='q': break con.acquire()##固定语法,如果执行notify必须有acquire和release。 con.notify(int(inp))##表示满足inp不等于q的条件,放行几个线程。 con.release() 0 1 2 3 4 5 6 7 8 9 >>1 >>100 2 >>101 102
上面:
con.acquire()
con.notify()
con.release()
为固定用法,表示用户输入为数字的时候,通过notify方法通知程序放行几个线程。
也可以根据函数的返回的布尔值来判断是否放行线程如下,每次只放行一个线程:
1 import threading
2 con=threading.Condition()#初始化一个Condition实例。
3
4 def m(): 5 inp=input('>') 6 if inp=='1': 7 return True 8 else: 9 return False 10 def func(c,i): 11 print(i) 12 c.acquire() 13 c.wait_for(m)##wait_for()1:可被调用的方法而且需要返回值是布尔型。如果返回为真放行一个线程。反之不放行。 14 print(i+100) 15 c.release() 16 17 18 19 for i in range(10): 20 t=threading.Thread(target=func,args=(con,i)) 21 t.start()
e:计数器,表示超时之后开始执行。
1 import threading
2
3 def func():
4 print(100) 5 6 7 t=threading.Timer(2,func)##2表示多久之后执行,func为可执行函数。 8 t.start()
e:线程池:
1:线程的复用,而不是重新创建一个线程。如果任务过多,重复的创建新的线程,上下文的开销(维护线程的开销)较多,资源浪费。
2:当线程池里的线程取完,下一次任务需要等待线程执行完,在执行。
如下是简单版本的多线程:
1 import threading
2 import queue
3 import time
4
5 class Thread_Poll:
6 q=queue.Queue()##定义队列。
7 def __init__(self,maxsize):
8 self.maxsize=maxsize#定义线程池队列的大小。
9
10 def put_thread(self):#往线程池里放线程类。
11 for i in range(self.maxsize):
12 Thread_Poll.q.put(threading.Thread)
13
14 def get_thread(self):#返回一个线程类。
15 return Thread_Poll.q.get()
16
17
18 def task(i):#执行的任务。
19 print(i)
20 time.sleep(2)
21 Thread_Poll.q.put(threading.Thread)#完成任务,需要补充线程池的线程。
22
23
24
25 if __name__=="__main__":
26 obj=Thread_Poll(5)
27 obj.put_thread()
28 for i in range(30):#执行任务。
29 t=obj.get_thread()(target=task,args=(i,))
30 t.start()
更新的第二版:
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 # Author:Alex Li
4 import queue
5 import threading
6 import contextlib
7 import time
8
9 StopEvent = object()#赋值。当任务等于他的时候,表示任务执行完毕。
10
11 class ThreadPool(object):
12
13 def __init__(self, max_num, max_task_num = None):
14 if max_task_num:#任务数。
15 self.q = queue.Queue(max_task_num)#创建队列,队列的长度为任务数。
16 else:
17 self.q = queue.Queue()#如果没有传入,创建一个没有限制的队列。
18 self.max_num = max_num#线程的最大数。
19 self.cancel = False#取消线程执行
20 self.terminal = False#取消线程执行。
21 self.generate_list = []#保存正在执行任务线程对象的列表。
22 self.free_list = []#保存空余线程的列表。
23
24 def run(self, func, args, callback=None):
25 """
26 线程池执行一个任务
27 :param func: 任务函数
28 :param args: 任务函数所需参数
29 :param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数1、任务函数执行状态;2、任务函数返回值(默认为None,即:不执行回调函数)
30 :return: 如果线程池已经终止,则返回True否则None
31 """
32 if self.cancel:#如果cancel=True表示任务执行完。
33 return
34 if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:#当没有空闲线程和正在运行的线程数量小于总线程大小。
35 self.generate_thread()#执行创建线程的方法。
36 w = (func, args, callback,)#创建一个元组。func任务函数、args表示任务函数参数。callback任务执行完回调函数(任务执行状态successful还是fail以及任务函数的返回值)。
37 self.q.put(w)#往队列里添加任务。
38
39 def generate_thread(self):
40 """
41 创建一个线程,并执行相应的call方法。
42 """
43 t = threading.Thread(target=self.call)
44 t.start()
45
46 def call(self):
47 """
48 循环去获取任务函数并执行任务函数
49 """
50 current_thread = threading.currentThread#获取当前的线程。
51 self.generate_list.append(current_thread)#把当前的线程放在列表中。
52
53 event = self.q.get()#获取任务。
54 while event != StopEvent:#任务不为空即:任务不等StopEvent。
55
56 func, arguments, callback = event#赋值。
57 try:
58 result = func(*arguments)#执行任务函数。
59 success = True#执行完,赋值表示执行成功。
60 except Exception as e:#报错,表示任务执行失败。
61 success = False
62 result = None
63
64 if callback is not None:#任务回调函数不为空。
65 try:
66 callback(success, result)#执行回调函数,参数为任务状态以及任务结果。
67 except Exception as e:#执行回调函数报错的情况处理。
68 pass
69
70 with self.worker_state(self.free_list, current_thread):
71 print(self.terminal)
72 if self.terminal:
73 event = StopEvent
74 else:
75 event = self.q.get()
76 else:
77
78 self.generate_list.remove(current_thread)
79
80 def close(self):
81 """
82 执行完所有的任务后,所有线程停止
83 """
84 self.cancel = True
85 full_size = len(self.generate_list)
86 while full_size:
87 self.q.put(StopEvent)
88 full_size -= 1
89
90 def terminate(self):
91 """
92 无论是否还有任务,终止线程
93 """
94 self.terminal = True
95
96 while self.generate_list:
97 self.q.put(StopEvent)
98
99 self.q.empty()
100
101 @contextlib.contextmanager
102 def worker_state(self, state_list, worker_thread):
103 """
104 用于记录线程中正在等待的线程数
105 """
106 state_list.append(worker_thread)
107 try:
108 yield
109 finally:
110 state_list.remove(worker_thread)
111
112 pool = ThreadPool(5)
113
114 def callback(status, result):
115 # status, execute action status
116 # result, execute action return value
117 pass
118
119 def action(i):
120 print(i)
121
122 for i in range(300):
123 ret = pool.run(action, (i,), callback)
124
125 # time.sleep(5)
126 # print(len(pool.generate_list), len(pool.free_list))
127 # print(len(pool.generate_list), len(pool.free_list))
更新第三版:
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 # Author:evil_liu
4 import queue
5 import threading
6 import contextlib
7 import time
8
9 StopEvent = object()#赋值。当任务等于他的时候,表示任务执行完毕。
10 RES_LIST=[]
11 class ThreadPool():
12 count=0
13 lock=threading.RLock()#线程锁
14 def __init__(self, max_num, max_task_num):
15 self.max_task_num=max_task_num# 任务计数。
16 if max_task_num:#任务数。
17 self.q = queue.Queue(max_task_num)#创建队列,队列的长度为任务数。
18 else:
19 self.q = queue.Queue()#如果没有传入,创建一个没有限制的队列。
20 self.req=queue.Queue(max_task_num)##结果队列。
21 self.max_num = max_num#线程的最大数。
22 self.cancel = False#取消线程执行
23 self.terminal = False#取消线程执行。
24 self.generate_list = []#保存正在执行任务线程对象的列表。
25 self.free_list = []#保存空余线程的列表。
26
27 def run(self, func, args, callback=None):
28 """
29 线程池执行一个任务
30 :param func: 任务函数
31 :param args: 任务函数所需参数
32 :param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数1、任务函数执行状态;2、任务函数返回值(默认为None,即:不执行回调函数)
33 :return: 如果线程池已经终止,则返回True否则None
34 """
35 if self.cancel:#如果cancel=True表示任务执行完。
36 return
37 if len(self.generate_list) < self.max_num:#当没有空闲线程和正在运行的线程数量小于总线程大小。
38 self.generate_thread()#执行创建线程的方法。
39 w = (func, args, callback,)#创建一个元组。func任务函数、args表示任务函数参数。callback任务执行完回调函数(任务执行状态successful还是fail以及任务函数的返回值)。
40 self.q.put(w)#往队列里添加任务。
41
42 def generate_thread(self):
43 """
44 创建一个线程,并执行相应的call方法。
45 """
46 t = threading.Thread(target=self.call)
47 t.start()
48
49 def call(self):
50 """
51 循环去获取任务函数并执行任务函数
52 """
53 current_thread = threading.currentThread#获取当前的线程。
54 self.generate_list.append(current_thread)#把当前的线程放在列表中。
55
56 event = self.q.get()#获取任务。
57 while event != StopEvent:#任务不为空即:任务不等StopEvent。
58
59 func, arguments, callback = event#赋值。
60 # if ThreadPool.count <= self.max_task_num:
61 try:
62 result = func(*arguments)#执行任务函数。
63 success = True#执行完,赋值表示执行成功。
64 except Exception as e:#报错,表示任务执行失败。
65 success = False
66 result = None
67 if callback is not None:#任务回调函数不为空。
68 try:
69 callback(success, result)#执行回调函数,参数为任务状态以及任务结果。
70 except Exception as e:#执行回调函数报错的情况处理。
71 pass
72
73 try:
74 event = self.q.get(timeout=2)#获取数据超时。超时之后表示任务执行完成。
75 except queue.Empty as e:
76 self.generate_list.remove(threading.currentThread)##释放线程
77 break
78 def close(self):
79 """
80 执行完所有的任务后,
81 """
82 self.cancel = True
83 full_size = self.max_num
84 while full_size:
85 self.q.put(StopEvent)
86 full_size -= 1
87
88 pool = ThreadPool(5,10)
89 # lock_1=threading.RLock()
90 def callback(status, result):
91 pass
92
93 def action(i):
94 print("Now mssion is %s"%i)
95 time.sleep(2)
96 return True
97
98 for i in range(10):
99 ret = pool.run(action, (i,), callback)
100 time.sleep(10)
101 print(len(pool.generate_list))
终极版:
1 #!/bin/env python
2 #author:evil_liu
3 #date:2016-7-21
4 #description: thread pool
5
6 import threading
7 import time
8 import queue
9
10 class Thread_Poll:
11 '''
12 功能:该类主要实现多线程,以及线程复用。
13 '''
14 def __init__(self,task_num,max_size):
15 '''
16 功能:该函数是初始化线程池对象。
17 :param task_num: 任务数量。
18 :param max_size: 线程数量。
19 :return:无。
20 '''
21 self.task_num=task_num
22 self.max_size=max_size
23 self.q=queue.Queue(task_num)#设置任务队列的。
24 self.thread_list=[]
25 self.res_q=queue.Queue()#设置结果队列。
26
27 def run(self,func,i,call_back=None):
28 '''
29 功能:该函数是线程池运行主函数。
30 :param func: 传入任务主函数。
31 :param *args: 任务函数参数,需要是元组形式。
32 :param call_back: 回调函数。
33 :return: 无。
34 '''
35 if len(self.thread_list)<self.max_size:#如果目前线程数小于我们定义的线程的个数,进行创建。
36 self.creat_thread()
37 misson=(func,i,call_back)#往任务队列放任务。
38 self.q.put(misson)
39
40 def creat_thread(self):
41 '''
42 功能:该函数主要是创建线程,并调用call方法。
43 :return: 无。
44 '''
45 t=threading.Thread(target=self.call)
46 t.start()
47
48 def call(self):
49 '''
50 功能:该函数是线程循环执行任务函数。
51 :return: 无。
52 '''
53 cur_thread=threading.currentThread
54 self.thread_list.append(cur_thread)
55 event=self.q.get()
56 while True:
57 func,args,cal_ba=event#获取任务函数。
58 try:
59 res=func(*args)#执行任务函数。注意参数形式是元组形式。
60 flag="OK"
61 except Exception as e:
62 print(e)
63 res=False
64 flag="fail"
65 # print(flag)
66 self.res(res,flag)#调用回调函数,将执行结果返回到队列中。
67 try:
68 event=self.q.get(timeout=2)#如果任务队列为空,获取任务超时2s超过2s线程停止执行任务,并退出。
69 except Exception:
70 self.thread_list.remove(cur_thread)
71 break
72 def res(self,res,status):
73 '''
74 功能:该方法主要是将执行结果方法队列中。
75 :param res: 任务函数的执行结果。
76 :param status: 执行任务函数的结果,成功还是失败。
77 :return: 无。
78 '''
79 da_res=(res,status)
80 self.res_q.put(da_res)
81
82 def task(x,y):
83 '''
84 功能:该函数主要需要执行函数。
85 :param x: 参数。
86 :return: 返回值1,表示执行成功。
87 '''
88 print(x)
89 return x+y
90 def wri_fil(x):
91 '''
92 功能:该函数主要讲结果队列中的结果写入文件中。
93 :param x: 任务长度。
94 :return: 无。
95 '''
96 while True:#将执行结果,从队列中获取结果并将结果写入文件中。
97 time.sleep(1)
98 if pool.res_q.qsize()==x:#当队列当前的长度等于任务执行次数,表示任务执行完成。
99 with open('1.txt','w') as f1:
100 for i in range(pool.res_q.qsize()):
101 try:
102 data=pool.res_q.get(timeout=2)
103 f1.write('mission result:%s,status:%s\n'%data)
104 except Exception:
105 break
106 break
107 else:
108 continue
109 if __name__ == '__main__':
110 pool=Thread_Poll(10,5)#初始化线程池对象。
111 for i in range(10):#循环任务。
112 pool.run(task,(1,2))
113 wri_fil(10)
以上版本在window pycharm运行有时候出现程序hang住无法结束。但是linux测试没有这个问题。
f:进程:
执行脚本的时候,别在window下创建多进程,因为python是调用os.fork创建,而window不支持,想执行需要用:if __name__=="__main__".
a:多进程的创建:
1 import multiprocessing
2
3 def test(x):
4 print(x)
5
6
7 if __name__ == '__main__':
8 p=multiprocessing.Process(target=test,args=(1,))
9 p.start()
1 import multiprocessing
2 import time
3 def test(x):
4 time.sleep(9)
5 print(x)
6
7
8 if __name__ == '__main__':
9 for i in range(2):
10 p=multiprocessing.Process(target=test,args=(i,))
11 p.start()
12 p.join(6)#这个参数的意思是:进程在这等待6秒,6秒之后就往下执行代码,后面的代码执行完,不终止。等待子进程执行完。不加该参数默认会等待进程执行完。
13 print('ok')
daemon=True表示进程不等待子进程执行完。结束程序。
1 import multiprocessing
2 import time
3 def test(x):
4 time.sleep(9)
5 print(x)
6
7
8 if __name__ == '__main__':
9 for i in range(2):
10 p=multiprocessing.Process(target=test,args=(i,))
11 p.daemon = True##注意位置,在start之前。
12 p.start()
13 print('ok')
进程之间的数据共享:
进程之间数据不共享,单独维护一个块系统资源(内存空间.....)来维护自己的数据资源。
1 import multiprocessing
2 LI=[]
3 def test(x,li):
4 li.append(x)
5 print(li)
6
7
8 if __name__ == '__main__':
9 for i in range(2):
10 p=multiprocessing.Process(target=test,args=(i,LI))
11 p.start()
12 [0]
13 [1]
数据共享可以用如下:Queue方法可以构造特殊的数据,该方法包含put、get等。
1 import multiprocessing
2 from multiprocessing.queues import Queue
3 li=Queue(20,ctx=multiprocessing)
4 def test(y,x):
5 y.put(x)
6 print(y.qsize())
7
8
9 if __name__ == '__main__':
10 for i in range(4):
11 p=multiprocessing.Process(target=test,args=(li,i))
12 p.start()
13 1
14 2
15 3
16 4
c:Array() 数组的方法。来构建进程共享数据:不常用。
1)数组需要指定类型。
1 'c': ctypes.c_char, 'u': ctypes.c_wchar,
2 'b': ctypes.c_byte, 'B': ctypes.c_ubyte,
3 'h': ctypes.c_short, 'H': ctypes.c_ushort,
4 'i': ctypes.c_int, 'I': ctypes.c_uint,
5 'l': ctypes.c_long, 'L': ctypes.c_ulong,
6 'f': ctypes.c_float, 'd': ctypes.c_double
7
8 类型对应表
2)数组需要指定长度。
1 import multiprocessing
2
3
4 def test(x,y):
5 x[y]=y+100
6 for i in x:
7 print(i)
8 print("------------------")
9
10 li=multiprocessing.Array('i',4)
11 if __name__ == '__main__':
12 for i in range(4):
13 p=multiprocessing.Process(target=test,args=(li,i))
14 p.start()
在大多数语言中:数组类型的数据,在内存中是连续的一块内存,需要指定数据的大小、类型(类型的不同的占有的位置大小也不一样,需要提前指定。)python 的列表,具有可扩展、伸缩。在内存中的数据块地址是不连续的。是因为在内存中,内存数据块是通过链表的形式进行查看。
就是说一个数据记录上个数据的内存地址和下个数据的内存地址。这样的方式进行查找。
常用的方式:用multiprocessing模块的Manger()方法,使用字典形式的进行数据更改,没有数据类型的限制。
1 import multiprocessing
2
3
4 def test(x,y):
5 x[y]=y+100
6 for i in x.items():
7 print(i)
8 print("------------------")
9 obj=multiprocessing.Manager()
10 t=obj.dict()
11 # li=multiprocessing.Array('i',4)
12 if __name__ == '__main__':
13 for i in range(4):
14 p=multiprocessing.Process(target=test,args=(t,i))
15 p.start()
16 p.join()##注意:需要加join等待子进程执行完。因为修改的是主进程的数据,主进程需要和子进程进行socket链接。如果不等子进程执行完,就执行子进程。会导致主进城和子进程通讯出现问题。
17 (0, 100)
18 ------------------
19 (0, 100)
20 (1, 101)
21 ------------------
22 (0, 100)
23 (1, 101)
24 (2, 102)
25 ------------------
26 (0, 100)
27 (1, 101)
28 (2, 102)
29 (3, 103)
30 ------------------
c:进程锁。
当进程的数据进行共享的时候,多个进程操作共享数据之后,获得数据的结果会出现混乱。
1 import multiprocessing
2 import time
3 LOCK=multiprocessing.Lock()
4 def test(x,l):
5 x[0]-=1
6 time.sleep(3)
7 print(x[0])
8 obj=multiprocessing.Manager()
9 t=obj.dict()
10 if __name__ == '__main__':
11 t[0]=4
12 for i in range(4):
13 p=multiprocessing.Process(target=test,args=(t,LOCK))
14 p.start()
15 p.join()#####################注意join位置。
16 0
17 0
18 0
19 0
1 import multiprocessing
2 import time
3 LOCK=multiprocessing.Lock()
4 def test(x,l):
5 x[0]-=1
6 time.sleep(3)
7 print(x[0])
8 obj=multiprocessing.Manager()
9 t=obj.dict()
10 if __name__ == '__main__':
11 t[0]=4
12 for i in range(4):
13 p=multiprocessing.Process(target=test,args=(t,LOCK))
14 p.start()
15 p.join()####################这个位置就是逐个执行。
16 3
17 2
18 1
加锁:同样进程也有进程普通锁、递归锁、event、condition等。和线程使用的方式类同!
1 import multiprocessing
2 import time
3 LOCK=multiprocessing.Lock()
4 def test(x,l):
5 x[0]-=1
6 time.sleep(3)
7 print(x[0])
8 obj=multiprocessing.Manager()
9 t=obj.dict()
10 if __name__ == '__main__':
11 t[0]=4
12 for i in range(4):
13 p=multiprocessing.Process(target=test,args=(t,LOCK))
14 p.start()
15 p.join()
16 3
17 2
18 1
19 0
1 import multiprocessing
2 import time
3 LOCK=multiprocessing.Semaphore(2)##信号量。
4
5 def test(x,l):
6 LOCK.acquire()
7 x[0]+=100
8 time.sleep(2)
9 l.release()
10 print(x[0])
11 obj=multiprocessing.Manager()
12 t=obj.dict()
13 t[0]=4
14 if __name__ == '__main__':
15 for i in range(4):
16 p=multiprocessing.Process(target=test,args=(t,LOCK))
17 p.start()
18 p.join()
19 104
20 104
21 304
22 304
event:
1 import multiprocessing
2 e=multiprocessing.Event()
3
4 def test(x,l):
5 x[0]+=100
6 l.wait()
7 print(x[0])
8 obj=multiprocessing.Manager()
9 t=obj.dict()
10 t[0]=4
11 for i in range(4):
12 p=multiprocessing.Process(target=test,args=(t,e))
13 p.start()
14 if __name__ == '__main__':
15 inp=input(">")
16 if inp=='1':
17 e.set()
18 >1
19 404
20 404
21 404
22 404
conditon()
1 import multiprocessing
2 e=multiprocessing.Condition()
3
4 def test(x,l):
5 l.acquire()
6 l.wait()
7 x[0]+=100
8 print(x[0])
9 l.release()
10 obj=multiprocessing.Manager()
11 t=obj.dict()
12 t[0]=4
13 for i in range(4):
14 p=multiprocessing.Process(target=test,args=(t,e))
15 p.start()
16 if __name__ == '__main__':
17 inp=input(">")
18 while True:
19 if inp!='q':
20 e.acquire()
21 e.notify(int(inp))
22 e.release()
d:进程池。
进程multiprocessing给我们提供了相应的方法。分别为:appy方法、app_async方法。
appy方法:并不是并发执行任务,而是当前进程执行完,才执行下个,串行执行。
1 import multiprocessing
2 import time
3
4
5 def task(args):
6 time.sleep(2)
7 print(args)
8
9
10 pool=multiprocessing.Pool(5)##初始化5个进程。
11 if __name__ == '__main__':
12 for i in range(10):
13 pool.apply(func=task,args=(i,))#从进程调用一个进程执行任务。
14 print("_____end_____")
15 0
16 1
17 2
18 3
19 4
20 5
21 6
22 7
23 8
24 9
25 _____end_____
app_async()异步执行(从队列获取任务,执行任务。),这个比较常用。可以看做是并发执行任务。
1 import multiprocessing
2 import time
3
4
5 def task(args):
6 time.sleep(1)
7 print(args)
8
9
10 pool=multiprocessing.Pool(5)##初始化5个进程。
11 if __name__ == '__main__':
12 for i in range(10):
13 pool.apply_async(func=task,args=(i,))#从进程调用一个进程执行任务。
14 pool.close()##执行close或者terminal方法,要不然会报断言错误。terminnal方法表示任务立即终止。
15 pool.join()#主进程等待子进程执行完任务。
16 print("_____end_____")
协程:
原理:多线程、多进程是系统执行的。而协程是程序员”操纵“。将一个线程分解成多个“微线程”,执行不同的任务。比如说:多次URL请求。每次URL请求耗时3S。如果用多线程执行的话,会出现资源浪费(维护、等待3s),可以用协程,我们可以让线程循环执行url请求,而不是在等待执行结果。也就说。可以在等待的同时执行其他的任务。
通过gevnet和greenlet方法执行。genvent是对greenlet的封装,底层还是调用greenlet。
安装:pip install gevent(安装这个自动安装greenlet)
1 from greenlet import greenlet
2
3
4 def test1():
5 print(12)
6 gr2.switch()
7 print(34)
8 gr2.switch()
9
10
11 def test2():
12 print(56)
13 gr1.switch()
14 print(78)
15
16 gr1 = greenlet(test1)
17 gr2 = greenlet(test2)
18 gr1.switch()
如上代码:当执行到gr1.switch()的时候,会执行test1函数,执行到test1函数到gr2.switch()的时候执行test2, 遵循这个规则依次执行下去。也就是说通过greenlet来控制线程的执行流。
1 import gevent
2
3 def foo():
4 print('Running in foo')
5 gevent.sleep(0)#执行切换。
6 print('Explicit context switch to foo again')
7
8 def bar():
9 print('Explicit context to bar')
10 gevent.sleep(0)#执行切换。
11 print('Implicit context switch back to bar')
12
13 gevent.joinall([
14 gevent.spawn(foo),
15 gevent.spawn(bar),
16 ])##创建协程。
17 Running in foo
18 Explicit context to bar
19 Explicit context switch to foo again
20 Implicit context switch back to bar
IO操作:执行http请求。
1 from gevent import monkey; monkey.patch_all()
2 import gevent
3 import requests
4
5 def f(url):
6 print('GET: %s' % url)
7 resp = requests.get(url)
8 data = resp.text
9 print('%d bytes received from %s.' % (len(data), url))
10
11 gevent.joinall([
12 gevent.spawn(f, 'https://www.python.org/'),
13 gevent.spawn(f, 'https://www.yahoo.com/'),
14 gevent.spawn(f, 'https://github.com/'),
15 ])#创建协程、循环执行这个url
16 GET: https://www.python.org/
17 GET: https://www.yahoo.com/
18 GET: https://github.com/
19 47394 bytes received from https://www.python.org/.
20 25528 bytes received from https://github.com/.
21 447316 bytes received from https://www.yahoo.com/.
monkey.patch_all()
底层封装socket,具有可以让线程可以一边执行url请求,一边可以收到url请求的结果。
应用场景:在python爬虫框架:scrapy就是用协程(gevent)来实现的,比较高效。
url存活检测(上线之后检测url的存活情况)。
一旦涉及到IO操作,比如http请求,gevent执行效率很高。
缓存:
本质:python通过socket连接服务端(缓存端)通过端口,建立连接 ,在服务端执行相应的命令。
安装memcache:
1:
1 wget http://memcached.org/latest
2 yum install -y libevent-devel.x86_64#64位操作系统。
3 ./configure
4 make && make install
虚拟机用的是网易的源。yum配置文件:
1 [base_server]
2 name=base_server
3 baseurl=http://mirrors.163.com/centos/6.8/os/x86_64/
4 enabled=1
5 gpgcheck=0
启动:
memcached -d -m 10 -u root -p 11211 -c 256 -P /tmp/memcached.pid
参数说明:
1 参数说明:
2 -d 是启动一个守护进程
3 -m 是分配给Memcache使用的内存数量,单位是MB
4 -u 是运行Memcache的用户
5 -l 是监听的服务器IP地址
6 -p 是设置Memcache监听的端口,最好是1024以上的端口
7 -c 选项是最大运行的并发连接数,默认是1024,按照你服务器的负载量来设定
8 -P 是设置保存Memcache的pid文件
memcache处理的数据类型:
a)key value形式:
1 #!/usr/bin/env python
2 #-*-coding:utf-8-*-
3 # author:liumeide
4 import memcache
5
6 mc = memcache.Client(['192.168.1.104:11211'], debug=True)
7 mc.set("OK", "evil")#设置键值对。
8 ret = mc.get('OK')#获取键值对。
9 print(ret)
10 evil
debug=True:表示运行过程中如果出现错误,显示错误信息。
b)支持集群操作:
python-memcached模块原生支持集群操作,其原理是在内存维护一个主机列表,且集群中主机的权重值和主机在列表中重复出现的次数成正比
1 主机 权重
2 10.10.1.1 3
3 10.10.1.2 2
4 10.10.1.3 1
5
6 那么在内存中主机列表为:
7 host_list = ["10.10.1.1", "10.10.1.2", "10.10.1.2", "10.10.1.3", ]
如果用户根据如果要在内存中创建一个键值对(如:k1 = "v1"),那么要执行一下步骤:
- 根据算法将 k1 转换成一个数字
- 将数字和主机列表长度求余数,得到一个值 N( 0 <= N < 列表长度 )
- 在主机列表中根据 第2步得到的值为索引获取主机,例如:host_list[N]
- 连接 将第3步中获取的主机,将 k1 = "v1" 放置在该服务器的内存中
代码实现如下:
1 mc = memcache.Client([('10.10.10.1:12000', 1), ('10.10.1.2:12000', 2), ('10.10.1.3:12000', 1)], debug=True)
2
3 mc.set('key1', 'value1')
c) add 添加一条键值对,如果有报错:MemCached: while expecting 'STORED', got unexpected response 'NOT_STORED'
1 mc = memcache.Client(['192.168.1.104:11211'], debug=True)
2 mc.add("ok", "tom")#添加键值对。
3 ret = mc.get('ok')#获取键值对。
4 print(ret)
5 tom
d)replace修改某个键值对。如果不存在报异常:MemCached: while expecting 'STORED', got unexpected response 'NOT_STORED'
1 import memcache
2
3 mc = memcache.Client(['192.168.1.104:11211'], debug=True)
4 mc.replace("ok", "come")#添加键值对。
5 ret = mc.get('ok')#获取键值对。
6 print(ret)
7 come
e)set修改键值对,如果不存在话创建,存在的话进行修改,这个一对一。set_multi 是对多个键值对进行修改。注意多个时候,里面放的是字典key:value。
1 import memcache
2
3 mc = memcache.Client(['192.168.1.104:11211'], debug=True)
4 mc.set("11", "come")#如果存在,修改,不存在创建。
5 mc.set_multi({"ok": "bye","cc":9})#如果存在,修改,不存在创建。
6 ret = mc.get('ok')#获取键值对。
7 ret1 = mc.get('11')#获取键值对。
8 print(ret,ret1)
9 bye come
f) delet delet_multi删除键值对,删除多个键值对。需要注意删除里面是key值而且是列表。
1 import memcache
2
3 mc = memcache.Client(['192.168.1.104:11211'], debug=True)
4 mc.delete("11")#如果存在,修改,不存在创建。
5 mc.delete_multi(["ok","cc"])#如果存在,修改,不存在创建
g) get 和get_multi获取键值对、多个键值对。
import memcache
mc = memcache.Client(['192.168.1.104:11211'], debug=True)
mc.set("11", "come")#如果存在,修改,不存在创建。
mc.set_multi({"ok": "bye","cc":9})#如果存在,修改,不存在创建。。
ret = mc.get('ok')#获取键值对。
ret1 = mc.get_multi(['11','ok'])#获取多个键值对。
print(ret,ret1)
bye {'11': 'come', 'ok': 'bye'}
h)append 和 prepend,append在修改指定的key值,在该值的后面追加内容,prepend则相反。
1 mc.append("11",22)
2 ret=mc.get("11")
3 print(ret)
4 come22
1 mc.prepend("11",22)
2 ret=mc.get("11")
3 print(ret)
4 22come22
i)decr和incr 将memcache中的一个值减少(默认是1)或者增加
1 ret=mc.get("cc")
2 mc.decr("cc")
3 ret1=mc.get("cc")
4 print(ret,ret1)
5 8 7
1 ret=mc.get("cc")
2 mc.incr("cc",2)
3 ret1=mc.get("cc")
4 print(ret,ret1)
5 7 9
j)cas和gets
比如说:我们在淘宝进行抢购商品的时候,没抢购一次,相应的存货应该自减,但是如果在相同时间内程序获取相同的存货数据,修改提交。在memcache中,会出现数据的不一致。
这样导致数据的混乱,cas和gets就是解决这个问题。类似线程的互斥锁的作用,memcache内部:每当程序来获取数据的时候,返回当前一个数字和对应的键值,当用户提交修改的时候。
memcache会把当前的数字和程序提交的数字进行比对,如果相等,说明这段时间内没有程序进行修改,如果不相等,提交失败。修改不了。
1 v=mc.gets("cc")
2 time.sleep(20)
3 #在gets和cas之间如果有提交数据,在下面的cas不成功。我测试没出现异常?也正常修改。。。。
4 mc.cas("cc",999)
安装redis
wget http://download.redis.io/releases/redis-3.0.6.tar.gz
tar xvzf redis-3.0.6.tar.gz
cd redis-3.0.6
make PREFIX=/export/server/redis install
启动:
1 nohup /export/server/redis/bin/redis-server &
安装API:
1 pip.exe install redis
redis针对数据类型:
1:字符串string.
2:hash 操作。
3:列表操作。
4:集合操作。
5:排序操作,比如说:([(11,1),(12,2)])根据后面第二值进行操作。
6:管道
7:发布订阅
a)操作:
添加值:
1 import redis
2
3 rc=redis.Redis(host="192.168.1.104",port=6379)
4 rc.set("test","ok")
5 v=rc.get("test")
6 print(str(v,encoding="utf-8"))
7 ok
注意:返回的结果是字节!!!!
b)连接方式:连接池
redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池
1 import redis
2
3 pool = redis.ConnectionPool(host='192.168.1.104', port=6379)
4 r = redis.Redis(connection_pool=pool)
5 r.set('ok', '11')
6 print(str(r.get('ok'),encoding="utf-8"))
7 11
c)操作:
字符串:redis中。字符串是按照一个name对应一个value来存储。
set(name, value, ex=None, px=None, nx=False, xx=False)
1 在Redis中设置值,默认,不存在则创建,存在则修改
2 参数:
3 ex,过期时间(秒)
4 px,过期时间(毫秒)
5 nx,如果设置为True,则只有name不存在时,当前set操作才执行
6 xx,如果设置为True,则只有name存在时,岗前set操作才执行
设置过期时间:
1 import redis
2
3 pool = redis.ConnectionPool(host='192.168.1.104', port=6379)
4 r = redis.Redis(connection_pool=pool)
5 r.setex('ok', '11',2)
6 print(str(r.get('ok'),encoding="utf-8"))
7 11
mset(*args, **kwargs) 批量设置
1 批量设置值
2 如:
3 mset(k1='v1', k2='v2')
4 或
5 mget({'k1': 'v1', 'k2': 'v2'})
1 import redis
2
3 pool = redis.ConnectionPool(host='192.168.1.104', port=6379)
4 r = redis.Redis(connection_pool=pool)
5 r.mset(a="2",b="4")
6 print(str(r.get("a"),encoding="utf-8"))
mget(keys, *args)批量获取值
1 批量获取
2 如:
3 mget('ylr', 'xi')
4 或
5 r.mget(['ylr', 'x'])
append(key, value)
1 # 在redis name对应的值后面追加内容
2
3 # 参数:
4 key, redis的name
5 value, 要追加的字符串
incrbyfloat(self, name, amount=1.0)
1 # 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
2
3 # 参数:
4 # name,Redis的name
5 # amount,自增数(浮点型)
decr(self, name, amount=1)
1 # 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。
2
3 # 参数:
4 # name,Redis的name
5 # amount,自减数(整数)
Hash操作:
存储:如下图:
hset(name, key, value)
1 # name对应的hash中设置一个键值对(不存在,则创建;否则,修改)
2
3 # 参数:
4 # name,redis的name
5 # key,name对应的hash中的key
6 # value,name对应的hash中的value
7
8 # 注:
9 # hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)
hmset(name, mapping)
1 # 在name对应的hash中批量设置键值对
2
3 # 参数:
4 # name,redis的name
5 # mapping,字典,如:{'k1':'v1', 'k2': 'v2'}
6
7 # 如:
8 # r.hmset('xx', {'k1':'v1', 'k2': 'v2'})
hget(name,key)
1 # 在name对应的hash中获取根据key获取value
hmget(name, keys, *args)
1 # 在name对应的hash中获取多个key的值
2
3 # 参数:
4 # name,reids对应的name
5 # keys,要获取key集合,如:['k1', 'k2', 'k3']
6 # *args,要获取的key,如:k1,k2,k3
7
8 # 如:
9 # r.mget('xx', ['k1', 'k2'])
10 # 或
11 # print r.hmget('xx', 'k1', 'k2')
List操作:
lpush(name,values)
1 # 在name对应的list中添加元素,每个新的元素都添加到列表的最左边
2
3 # 如:
4 # r.lpush('oo', 11,22,33)
5 # 保存顺序为: 33,22,11
6
7 # 扩展:
8 # rpush(name, values) 表示从右向左操作
1 import redis
2
3 pool = redis.ConnectionPool(host='192.168.1.104', port=6379)
4 r = redis.Redis(connection_pool=pool)
5 r.lpush("oo",[1,2,3])
lpushx(name,value)
1 # 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
2
3 # 更多:
4 # rpushx(name, value) 表示从右向左操作
1 import redis
2
3 pool = redis.ConnectionPool(host='192.168.1.104', port=6379)
4 r = redis.Redis(connection_pool=pool)
5 # r.lpush("oo",[1,2,3])
6 r.lpush("oo",4)
llen(name)
1 # name对应的list元素的个数
1 import redis
2
3 pool = redis.ConnectionPool(host='192.168.1.104', port=6379)
4 r = redis.Redis(connection_pool=pool)
5 r.lpushx("oo",5)
6 print(r.llen("oo"))
7 4
linsert(name, where, refvalue, value))
1 # 在name对应的列表的某一个值前或后插入一个新值
2
3 # 参数:
4 # name,redis的name
5 # where,BEFORE或AFTER
6 # refvalue,标杆值,即:在它前后插入数据
7 # value,要插入的数据
r.lset(name, index, value)
1 # 对name对应的list中的某一个索引位置重新赋值
2
3 # 参数:
4 # name,redis的name
5 # index,list的索引位置
6 # value,要设置的值
r.lrem(name, value, num)
1 # 在name对应的list中删除指定的值
2
3 # 参数:
4 # name,redis的name
5 # value,要删除的值
6 # num, num=0,删除列表中所有的指定值;
7 # num=2,从前到后,删除2个;
8 # num=-2,从后向前,删除2个
1 import redis
2
3 pool = redis.ConnectionPool(host='192.168.1.104', port=6379)
4 r = redis.Redis(connection_pool=pool)
5 r.lrem("oo",5,-1)
6 print(r.llen("oo"))
7 3
常用操作:
delete(*names):根据删除redis中的任意数据类型
exists(name):检测redis的name是否存在
keys(pattern='*'):
1 # 根据模型获取redis的name
2
3 # 更多:
4 # KEYS * 匹配数据库中所有 key 。
5 # KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
6 # KEYS h*llo 匹配 hllo 和 heeeeello 等。
7 # KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo
expire(name ,time):为某个redis的某个name设置超时时间
rename(src, dst): 对redis的name重命名为
move(name, db)): 将redis的某个值移动到指定的db下
randomkey():随机获取一个redis的name(不删除)
type(name): 获取name对应值的类型
scan(cursor=0, match=None, count=None)
scan_iter(match=None, count=None)
同字符串操作,用于增量迭代获取key
c:管道:
如果在redis操作中,执行多个命令的时候可以用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作。
1 import redis
2
3 pool = redis.ConnectionPool(host='192.168.1.104', port=6379)
4
5 r = redis.Redis(connection_pool=pool)
6
7 pipe = r.pipeline(transaction=True)
8
9 r.set('test', 'ok')
10 r.set('test1', 'cc')
11 print(r.get("test"))
12 print(r.get("test1"))
13
14 pipe.execute()
15 b'ok'
16 b'cc'
d:发布订阅:
小样:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import redis
class RedisHelper:
def __init__(self):
self.__conn = redis.Redis(host='10.211.55.4')
self.chan_sub = 'fm104.5'
self.chan_pub = 'fm104.5'
def public(self, msg):
self.__conn.publish(self.chan_pub, msg)
return True
def subscribe(self):
pub = self.__conn.pubsub()
pub.subscribe(self.chan_sub)
pub.parse_response()
return pub
订阅者:
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 from monitor.RedisHelper import RedisHelper
5
6 obj = RedisHelper()
7 redis_sub = obj.subscribe()
8
9 while True:
10 msg= redis_sub.parse_response()
11 print msg
发布者:
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 from monitor.RedisHelper import RedisHelper
5
6 obj = RedisHelper()
7 obj.public('hello')