一 线程
线程是应用程序中工作的最小单元。
python的线程中没有优先级、线程组,也不能被停止、暂停、恢复、中断,线程只能随着线程中的代码执行完毕而被销毁。查了n多资料之后终于接受了以上事实,个人觉得这是python的一个坑,导致了我在实现线程池的时候无法停止已经注入了方法且执行超时的线程。
threading模块提供的类:
Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, local.
threading 模块提供的常用方法:
threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
通过threading模块创建线程有两种方式,如下:
方式1:
#!/usr/bin/env python # coding=utf-8 import threading def f1(arg): print(arg) if __name__ == '__main__': t1 = threading.Thread(target=f1, args=(1,)) t1.start() # 输出 # 1
start()线程准备就绪,等待cpu调度
setName 为线程设置名称
getName 获取线程名称
run() 线程被cpu调度后自动执行线程对象的run方法
方法2:
class MyThread(threading.Thread): def __init__(self, func, args): self.func = func self.args = args super(MyThread, self).__init__() def run(self): self.func(self.args) def f2(arg): print(arg) obj = MyThread(f2, 123) obj.start()
# 输出:
# 123
setDaemon方法
主线程不等子线程执行结束就退出
#!/usr/bin/env python # coding=utf-8 import threading import time def f1(arg): time.sleep(2) print('start', arg) if __name__ == '__main__': t1 = threading.Thread(target=f1, args=(1,)) t1.setDaemon(True) t1.start() print(123)
# 输出:
# 123 而不是 1 12
join方法
逐个执行每个线程,执行完毕后继续往下执行,该方法使多线程变得无意义。
表示主线程等待子线程执行完毕,join加参数表示最多等几秒
#!/usr/bin/env python # coding=utf-8 import threading import time def f1(arg): time.sleep(2) print('start', arg) if __name__ == '__main__': t1 = threading.Thread(target=f1, args=(1,)) t1.setDaemon(True) t1.start() t1.join() print(123)
# 输出:
# 1 123
线程锁
由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁 - 同一时刻允许一个线程执行操作。
acquire 给线程上锁
release 给线程解锁
无论是lock还是rlock,提供的方法都非常简单,acquire和release。但是rlock和lock的区别是什么呢?RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。注意:如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的锁。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # coding=utf-8 #!/usr/bin/env python # coding=utf-8 import threading import time NUM = 10 def func(): global NUM NUM -= 1 time.sleep(2) print(NUM) for i in range(10): t = threading.Thread(target=func) t.start() # 输出10个0
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # coding=utf-8 import threading import time NUM = 10 def func(l, i): global NUM l.acquire() NUM -= 1 time.sleep(1) print(i, NUM) l.release() lock = threading.RLock() # lock = threading.BoundedSemaphore(5) for i in range(10): t = threading.Thread(target=func, args=(lock, i)) t.start() # 输出 0 9 1 8 2 7 3 6 4 5 5 4 6 3 7 2 8 1 9 0
信号量
互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # coding=utf-8 import threading import time NUM = 10 def func(l, i): global NUM l.acquire() NUM -= 1 time.sleep(1) print(i, NUM) l.release() lock = threading.BoundedSemaphore(3) for i in range(10): t = threading.Thread(target=func, args=(lock, i)) t.start() # 输出 0 7 1 7 2 7 5 4 4 3 3 2 6 1 7 1 8 1 9 0
事件
python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。
事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
- clear:将“Flag”设置为False
- set:将“Flag”设置为True
- wait:检测“Flag”是True或False
- is_set:返回当前‘Flag’
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # coding=utf-8 import threading def func(i, e): print(i) e.wait() # 检查是true或false, false等,true通过 print(i+100) event = threading.Event() for i in range(10): t = threading.Thread(target=func, args=(i, event,)) t.start() event.clear() # 设置为false inp = input('>>') if inp == '1': event.set() # 设置为true
条件(Condition)
使得线程等待,只有满足某条件时,才释放n个线程
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # coding=utf-8 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)) c.release()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # coding=utf-8 import threading def condition(): ret = False r = input('>>>') if r == 'true': ret = True else: ret = False return ret def func(i, con): print(i) con.acquire() con.wait_for(condition) print(i+100) con.release() c = threading.Condition() for i in range(10): t = threading.Thread(target=func, args=(i, c)) t.start()
Timer
定时器,指定n秒后执行某操作
#!/usr/bin/env python # coding=utf-8 from threading import Timer def hi(): print('hi') t = Timer(1, hi) t.start() # 一秒后才执行hi函数
进程
#!/usr/bin/env python # coding=utf-8 from multiprocessing import Process def foo(i): print(i) for i in range(10): p = Process(target=foo,args=(i,)) # p.daemon = True p.start() # p.join()
进程间共享数据
不同进程间内存是不共享的,线程是共享的,要想实现两个进程间的数据交换,可以用以下方法
没有数据共享
#!/usr/bin/env python # coding=utf-8 from multiprocessing import Process def foo(i, arg): arg.append(i) print(i, arg) if __name__ == '__main__': li = [] for i in range(3): p = Process(target=foo, args=(i,li,)) p.start() p.join() # 输出 # 0 [0] # 1 [1] # 2 [2] # 如果通信应该输出 # 0 [0] # 1 [0,1] # 2 [0,1,2]
方式一:
#!/usr/bin/env python # coding=utf-8 from multiprocessing import Process from multiprocessing import queues import multiprocessing def foo(i, arg): arg.put(i) print(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() # 输出 # 0 1 # 1 2 # 2 3 # 3 4 # 4 5 # 5 6 # 6 7 # 7 8 # 8 9 # 9 10
方式二:
#!/usr/bin/env python # coding=utf-8 from multiprocessing import Process from multiprocessing import Array def foo(i, arg): arg[i] = i + 100 for item in arg: print(item,) if __name__ == '__main__': li = Array('i', 3) for i in range(3): p = Process(target=foo, args=(i,li,)) p.start() p.join() # 输出 # 100 # 0 # 0 # 100 # 101 # 0 # 100 # 101 # 102
方式三:
#!/usr/bin/env python # coding=utf-8 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(3): p = Process(target=foo, args=(i,li,)) p.start() p.join() # 输出 # [100] # [100, 101] # [100, 101, 102]
进程锁
和线程锁的方法几乎一致,用法也一致
不使用锁
#!/usr/bin/env python # coding=utf-8 from multiprocessing import Process from multiprocessing import Array from multiprocessing import RLock, Lock, Event, Condition, Semaphore import time def foo(i, arg): arg[0] -= 1 time.sleep(1) print(arg[0]) if __name__ == '__main__': li = Array('i', 3) li[0] = 3 for i in range(3): p = Process(target=foo, args=(i,li,)) p.start() # 输出 # 0 # 0 # 0
使用锁
#!/usr/bin/env python # coding=utf-8 from multiprocessing import Process from multiprocessing import Array from multiprocessing import RLock, Lock, Event, Condition, Semaphore import time def foo(i, arg): arg.acquire() arg[0] -= 1 time.sleep(1) print(arg[0]) arg.release() if __name__ == '__main__': li = Array('i', 3) li[0] = 3 lock = RLock() for i in range(3): p = Process(target=foo, args=(i,li,)) p.start() # 输出 # 2 # 1 # 0
进程池
进程创建子进程的过程,子进程克隆了一遍父进程里的数据,如果父进程占用空间特别大,子进程启动过多就会导致系统空间不够用,所以引出了进程池的概念;进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有两个方法:
- apply 同步执行(串行)
- apply_async 异步执行(并行)
#!/usr/bin/env python # coding=utf-8 from multiprocessing import Process, Pool import time def f1(arg): print(arg) pool = Pool(5) for i in range(30): pool.apply(func=f1, args=(i,))
#!/usr/bin/env python # coding=utf-8 from multiprocessing import Process, Pool import time def f1(arg): time.sleep(1) print(arg) pool = Pool(5) for i in range(30): pool.apply_async(func=f1, args=(i,)) pool.close() pool.join() print('end')
队列
队列包括先进先出(FIFO)、后进先出(LIFO)、优先级队列和双向队列。
put() 放数据
get()取数据
qsize() 队列元素个数
maxsize() 队列最大个数
empty() 队列是否为空
full() 队列是否满了
join() 队列中所有项目被去除并处理后,阻塞
task_done() 表示完成一个 task,并递减没有完成的队列数,当队列全部完成时候,没有task可执行,因此需要发送一个信号,通知被阻塞的主线程,继续运行。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # coding=utf-8 import queue q = queue.Queue(2) # 队列最大长度位2 q.put(1) q.put(2) #q.put(3, block=False, timeout=2) # 超时时间为2秒,超过时间没有位置,报错 block=False表示不阻塞,没有位置直接报错 #print(q.qsize()) print(q.get()) print(q.get()) print(q.empty()) #print(q.get(timeout=2)) # 两秒还娶不到数据,抛出Empty异常 print(q.get(block=False)) # 娶不到,直接报错
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # coding=utf-8 import queue q = queue.Queue(5) q.put(1) q.put(2) print(q.get()) q.task_done() print(q.get()) q.task_done() q.join()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # coding=utf-8 import queue q = queue.LifoQueue() q.put(123) q.put(456) print(q.get())
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # coding=utf-8 q = queue.PriorityQueue() q.put((1, 'hexm')) q.put((2,'sb')) q.put((0, 'jj')) print(q.get())
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
q = queue.deque() q.append(123) q.append(323) q.appendleft(456) q.pop() q.popleft()