python进程线程
1 什么是线程
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
一个线程其实就是一堆指令集合。竞争式抢占CPU资源。
1 import time 2 import threading 3 4 begin = time.time() 5 def foo(n): 6 print('foo-%s' % n) 7 time.sleep(1) 8 print('end foo') 9 10 11 def bar(n): 12 print('bar-%s' % n) 13 time.sleep(2) 14 print('end bar') 15 16 17 # foo() 18 # bar() 19 t1 = threading.Thread(target=foo, args=(1,)) 20 t2 = threading.Thread(target=bar, args=(2,)) 21 22 t1.start() 23 t2.start() 24 print('----- in the main-----') 25 26 t1.join() 27 t2.join() 28 29 end = time.time() 30 print(end-begin)
2 什么是进程
进程是对线程资源的整合,一个程序实例。
IO密集型任务或函数,计算密集型任务函数。
3 GIL
同一时刻,只能有有一个线程进入解释器。即保证同一时刻只有一个线程对共享资源进行存取。
在python中,如果处理任务是IO密集型,可用多线程;如果是计算密集型的,需要使用C语言。
4 进程与线程的区别
进程里面的线程可共享数据。
线程之间可通信,进程不能。
进程创建通过拷贝,比较消耗CPU资源。
join()在子线程完成运行之前,这个子线程的父线程将一直被阻塞。
setDaemon(True)与join()方法相反。守护主线程。
1 import threading 2 from time import ctime, sleep 3 import time 4 5 def music(func): 6 for i in range(2): 7 print('开始播放音乐%s. %s' % (func, ctime())) 8 sleep(4) 9 print('结束音乐播放%s' % ctime()) 10 11 12 def move(func): 13 for i in range(2): 14 print('开始播放电影%s. %s' % (func, ctime())) 15 sleep(5) 16 print('结束电影播放%s' % ctime()) 17 18 19 threads = [] 20 t1 = threading.Thread(target=music, args=('七里香',)) 21 threads.append(t1) 22 t2 = threading.Thread(target=move, args=('教父',)) 23 threads.append(t2) 24 25 26 if __name__ == "__main__": 27 for t in threads: 28 t.setDaemon(True) # 守护作用,子线程无论是否完成,都所主线程结束而结束 29 t.start() 30 # t.join() # 没有线程效果 31 # t.join() # t2执行完成后,才会执行下方代码 32 # t1.join() # t1执行完成后,执行下方代码。 33 print(threading.current_thread()) 34 print(threading.active_count()) 35 print('播放结束%s' % ctime())
1 import threading 2 import time 3 4 class MyThread(threading.Thread): 5 def __init__(self, num): 6 threading.Thread.__init__(self) 7 self.num = num 8 9 # 定义每个线程要运行的函数 10 def run(self): 11 print("运行数字:%s" % self.num) 12 time.sleep(3) 13 14 15 if __name__ == '__main__': 16 t1 = MyThread(1) 17 t2 = MyThread(2) 18 t1.start() 19 t2.start()
5 同步锁
同步锁也是保证同一时刻只有一个线程被执行。
1 import time 2 import threading 3 4 5 def addNum(): 6 global num 7 # num -= 1 # CPU还未切换,就已经减一操作了 8 r.acquire() # 获取锁对象 9 temp = num # 多个线程时,不安全,还未减一CPU就切换了 10 time.sleep(0.00001) 11 print('--get num:', num) 12 num = temp - 1 13 r.release() # 释放锁对象 14 15 16 # 17 num = 100 18 thread_list = [] 19 r = threading.Lock() # 和join类似 20 for i in range(100): 21 t = threading.Thread(target=addNum) 22 t.start() 23 thread_list.append(t) 24 25 for t in thread_list: 26 t.join() # join是等待线程结束再开启其它线程 27 28 print('final num:', num) 29 # GIL保证同一时刻,保证只有一个线程进入解释器 30 # Lock避免CPU快速切换造成数据误差
6 递归锁和死锁
死锁:再线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源。解决办法是使用递归锁。
递归锁:threading.Rlock(),Rlock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其它的线程才能获得资源。
1 import threading 2 import time 3 4 class MyThread(threading.Thread): 5 def doA(self): 6 lock.acquire() # A 7 print(self.name, 'gotlockA', time.ctime()) 8 time.sleep(3) 9 # 内部已经A锁,为什么还要有B锁? 10 # 防止其它函数修改数据 11 lock.acquire() # B 12 print(self.name, 'gotlockB', time.ctime()) 13 lock.release() # B 14 lock.release() # A 15 16 def doB(self): 17 lock.acquire() # B 18 print(self.name, 'gotlockB', time.ctime()) 19 time.sleep(2) 20 lock.acquire() # A 21 print(self.name, 'gotlockA', time.ctime()) 22 lock.release() # A 23 lock.release() # B 24 25 def run(self): 26 self.doA() 27 time.sleep(1) 28 self.doB() 29 30 31 # 就如同进小区大门后锁小区大门,再进家门后锁家门 32 if __name__ == "__main__": 33 # lockA = threading.Lock() 34 # lockB = threading.Lock() 35 lock = threading.RLock() # 递归锁,内部有计时器和锁两个东西 36 threads = [] 37 for i in range(5): 38 threads.append(MyThread()) 39 for t in threads: 40 t.start() 41 for t in threads: 42 t.join()
7 信号量
信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数器,每当调用acquire()时-1,调用release()时+1。计数器不能小于0,当计数器为0时,acquire()将阻塞线程至同步锁定状态,知道其它线程调用release()。
并行锁,开几把锁 ,就会有几个线程进锁。类似停车位,4个停车位只能进来停4辆车。
1 import threading, time 2 3 4 class MyThreaad(threading.Thread): 5 def run(self): 6 if semaphore.acquire(): 7 print(self.name) 8 time.sleep(3) 9 semaphore.release() 10 11 12 # 用处,限制多少个线程连接数据库 13 if __name__ == "__main__": 14 semaphore = threading.BoundedSemaphore(5) # 决定有多少个线程 15 thrs = [] 16 for i in range(13): 17 thrs.append(MyThreaad()) 18 for t in thrs: 19 t.start()
8 条件变量同步(Condition)
有一类线程需要满足条件后才能够继续执行,python提供了threading.Condition对象用于条件变量线程的支持,例如生产者和消费者模型
lock_con = threading.Condition(Lock/Rlock),锁是可选项,不传人锁,对象自动创建一个Rlock()
wait():条件不满足时调用,线程会释放锁并进入等待阻塞。
notify():条件闯到后调用,通知等待池激活一个线程。
notifyAlll():条件创造后调用,通知等待池激活所有线程。
1 import threading, time 2 from random import randint 3 4 5 class Producer(threading.Thread): 6 def run(self): 7 global L 8 while True: 9 val = randint(0, 100) 10 print('生产者', self.name, ':Append'+str(val), L) 11 if lock_con.acquire(): 12 L.append(val) 13 lock_con.notify() 14 lock_con.release() 15 time.sleep(3) 16 17 18 class Consumer(threading.Thread): 19 def run(self): 20 global L 21 while True: 22 lock_con.acquire() 23 if len(L) == 0: 24 lock_con.wait() 25 print('消费者', self.name, ':Delete'+str(L[0]), L) 26 del L[0] 27 lock_con.release() 28 time.sleep(0.25) 29 30 31 if __name__ == '__main__': 32 L = [] 33 lock_con = threading.Condition() 34 threads = [] 35 for i in range(5): 36 threads.append(Producer()) 37 threads.append(Consumer()) 38 for t in threads: 39 t.start() 40 for t in threads: 41 t.join()
9 同步条件(event)
类似condition,不需要锁,比condition用的更广。
event = threading.Event(),初始值为False
event.isSet():返回event的状态值。
event.wait():如果event.isSet() == False将阻塞线程
event.set():设置event的状态值为True,所有的阻塞池的线程激活进入就绪状态,等待操作系统调度。
event.clear():恢复event状态值为False
1 import threading, time 2 3 4 class Boss(threading.Thread): 5 def run(self): 6 print('Boss:今晚大家都要加班到22:00') 7 event.isSet() or event.set() 8 time.sleep(5) 9 print('Boss:<22:00可以下班了>') 10 event.isSet() or event.set() 11 12 13 class Worker(threading.Thread): 14 def run(self): 15 event.wait() 16 print('Worker:要加班了') 17 time.sleep(0.25) 18 event.clear() 19 event.wait() 20 print('Worker:下班了') 21 22 23 if __name__ == '__main__': 24 event = threading.Event() 25 threads = [] 26 for i in range(5): 27 threads.append(Worker()) 28 threads.append(Boss()) 29 for t in threads: 30 t.start() 31 for t in threads: 32 t.join()
1 import threading, time 2 import random 3 4 5 def light(): 6 if not event.isSet(): 7 event.set() 8 count = 0 9 while True: 10 if count < 10: 11 print('10绿灯亮') 12 elif count < 13: 13 print('13黄灯亮') 14 elif count < 20: 15 if event.isSet(): 16 event.clear() 17 print('20红灯亮') 18 else: 19 count = 0 20 event.set() 21 time.sleep(1) 22 count += 1 23 24 25 def car(n): 26 while True: 27 time.sleep(random.randrange(10)) 28 if event.isSet(): 29 print('car [%s] is running...' % n) 30 else: 31 print('car [%s] is waiting for red light..' % n) 32 33 34 if __name__ == '__main__': 35 event = threading.Event() 36 Light =threading.Thread(target=light) 37 Light.start() 38 for i in range(3): 39 t = threading.Thread(target=car, args=(i,)) 40 t.start()
10 队列(queue)多线程利器
队列是一种数据结构,它的优势是本身有一把锁。
队列的模式:
先进先出FIFO,类似管道,queue.Queue(maxsize)。
先进后出LIFO,类似堆栈,queue.LifoQueue(maxsize).
优先队列,queue.Priority(maxsize)。
队列的方法:
put(item, block),item为必须插入项目的值,block默认值为1,如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元,如果block为0,put()方法将引发Full异常。
get([,block, timeout]),可选参数为block,默认为True。如果队列为空且block为True,get()就使调用吸纳成暂停,直至项目可用,如果队列为空且block为False,队列将引发Empty一场。timeout等待时间。
qsize()返回队列大小。
empty()如果队列为空,返回True,反之False。
full(),如果队列满了,返回True,反之False。
get_nowait(),相当get(False)。
put_nowait(item) ,相当put(item, False)。
task_done() ,在完成一项工作之后,task_done() 函数向任务已经完成的队列发送一个信号。
join() 实际上意味着等到队列为空,再执行别的操作。
1 import threading, queue 2 from time import sleep 3 from random import randint 4 5 6 class Production(threading.Thread): 7 def run(self): 8 while True: 9 r = randint(0, 100) 10 q.put(r) 11 print('生产出来%s号包子' % r) 12 sleep(1) 13 14 15 class Proces(threading.Thread): 16 def run(self): 17 while True: 18 re = q.get() 19 print('吃掉%s号包子' % re) 20 21 22 if __name__ == "__main__": 23 q = queue.Queue(10) 24 threads = [Production(), Production(), Production(), Proces()] 25 for t in threads: 26 t.start()
1 import time, random 2 import queue, threading 3 4 5 q = queue.Queue() 6 def Producer(name): 7 count = 0 8 while count < 20: 9 time.sleep(random.randrange(3)) 10 q.put(count) 11 print('生产者%s生产%s号包子..' % (name, count)) 12 count += 1 13 14 15 def Consumer(name): 16 count = 0 17 while count < 20: 18 time.sleep(random.randrange(4)) 19 if not q.empty(): 20 data = q.get() 21 print(data) 22 print('消费者%s吃了%s号包子' % (name, data)) 23 else: 24 print('没有包子可吃啊') 25 count += 1 26 27 28 p1 = threading.Thread(target=Producer, args=('A', )) 29 c1 = threading.Thread(target=Consumer, args=('B', )) 30 p1.start() 31 c1.start()
1 import random, threading, time 2 from queue import Queue 3 4 5 class Producer(threading.Thread): 6 def __init__(self, t_name, queue): 7 threading.Thread.__init__(self, name=t_name) 8 self.data = queue 9 10 def run(self): 11 for i in range(10): 12 randomnum = random.randint(1, 99) 13 print("%s: %s is producing %d to the queue!" % (time.ctime(), self.getName(), randomnum)) 14 self.data.put(randomnum) 15 time.sleep(1) 16 print("%s: %s finished!" %(time.ctime(), self.getName())) 17 18 19 class Consumer_even(threading.Thread): 20 def __init__(self, t_name, queue): 21 self.data = queue 22 23 def run(self): 24 while True: 25 try: 26 val_even = self.data.get(1, 5) 27 if val_even % 2 == 0: 28 print("%s: %s is consuming. %d in the queue is consumed!" % (time.ctime(),self.getName(),val_even)) 29 time.sleep(2) 30 else: 31 self.data.put(val_even) 32 time.sleep(2) 33 except: 34 print("%s: %s finished!" % (time.ctime(), self.getName())) 35 break 36 37 38 class Consumer_odd(threading.Thread): 39 def __init__(self, t_name, queue): 40 threading.Thread.__init__(self, name=t_name) 41 self.data = queue 42 43 def run(self): 44 while True: 45 try: 46 val_odd = self.data.get(1, 5) 47 if val_odd %2 != 0: 48 print("%s: %s is consuming. %d in the queue is consumed!" % (time.ctime(), self.getName(), val_odd)) 49 time.sleep(2) 50 else: 51 self.data.put(val_odd) 52 time.sleep(2) 53 except: 54 print("%s: %s finished!" % (time.ctime(), self.getName())) 55 break 56 57 58 def main(): 59 queue = Queue() 60 producer = Producer('Pro.', queue) 61 consumer_even = Consumer_even('Con_even.', queue) 62 consumer_odd = Consumer_odd('Con_odd.', queue) 63 producer.start() 64 consumer_even.start() 65 consumer_odd.start() 66 producer.join() 67 consumer_even.join() 68 consumer_odd.join() 69 print('所有线程执行完毕') 70 71 72 if __name__ == '__main__': 73 main()
11 进程
multiprocessing模块中Process类中调用
1 # Process类调用 2 3 from multiprocessing import Process 4 import time 5 6 7 def func(name): 8 print('hello', name,time.ctime()) 9 time.sleep(1) 10 11 12 if __name__ == "__main__": # 运行进程程序务必加上此句 13 p_list = [] 14 for i in range(3): 15 p = Process(target=func, args=('alvin:%s' % i,)) 16 p_list.append(p) 17 p.start() 18 for i in p_list: 19 i.join() 20 print("end")
1 # 创建类式的调用 2 from multiprocessing import Process 3 import time 4 5 6 class MyProcess(Process): 7 def __init__(self): 8 super(MyProcess, self).__init__() 9 10 def run(self): 11 time.sleep(1) 12 print('hello', self.name, time.ctime()) 13 14 15 if __name__ == "__main__": 16 p_list = [] 17 for i in range(3): 18 p = MyProcess() 19 p.start() 20 p_list.append(p) 21 22 for p in p_list: 23 p.join() 24 25 print('end')
Process(group=None, target=None, name=None, args=(), kwargs={})
方法:run();start()调用run()方法;join()阻塞当前上下文环境,直到调用此方法的进程终止或到达指定的timeout(可选参数);terminate()不管任务是否完成,立即停止工作进程;is_alive()返回进程是否在运行。
进程的名字解析
1 # 进程之间的关系 2 from multiprocessing import Process 3 import os 4 import time 5 6 7 def info(name): 8 print('name:', name) 9 print('父进程:', os.getppid()) 10 print('当前进程:', os.getpid()) 11 print('------------') 12 time.sleep(1) 13 14 15 def func(name): 16 info(name) 17 18 19 if __name__ == "__main__": 20 info('主进程') 21 p1 = Process(target=info, args=('alvin', )) 22 p2 = Process(target=func, args=('egon', )) 23 p1.start() 24 p2.start() 25 26 p1.join() 27 p2.join() 28 29 print('ending')
进程之间通信
1 # 进程间通讯队列Queues 2 from multiprocessing import Process, Queue 3 4 5 def func(q, n): 6 q.put([42, n, 'hello']) 7 8 9 if __name__ == "__main__": 10 q = Queue() 11 p_list = [] 12 for i in range(3): 13 p = Process(target=func, args=(q, i)) 14 p_list.append(p) 15 p.start() 16 print(q.get()) 17 print(q.get()) 18 print(q.get()) 19 for i in p_list: 20 i.join()
1 # 进程间通信Pipe 2 from multiprocessing import Process, Pipe 3 4 5 def func(conn): 6 conn.send([42, None, 'hello']) 7 print(conn.recv()) 8 conn.close() 9 10 11 if __name__ == "__main__": 12 parent_conn, child_conn = Pipe() 13 p = Process(target=func, args=(child_conn,)) 14 p.start() 15 print(parent_conn.recv()) 16 parent_conn.send("hello") 17 p.join()
进程间数据共享
Queue和pipe只是实现了数据交互,并没实现数据共享,即一个进程去更改另一个进程的数据。
1 # manager实现数据共享 2 from multiprocessing import Process, Manager 3 4 5 def func(d, l, n): 6 d[n] = n 7 d["name"] = "alvin" 8 l.append(n) 9 10 11 if __name__ == "__main__": 12 with Manager() as manager: 13 d = manager.dict() 14 l = manager.list(range(5)) 15 p_list = [] 16 for i in range(10): 17 p = Process(target=func, args=(d, l, i)) 18 p.start() 19 p_list.append(p) 20 for res in p_list: 21 res.join() 22 print(d) 23 print(l)
进程池
1 # 进程池 2 from multiprocessing import Pool 3 import time 4 5 6 def foo(args): 7 time.sleep(1) 8 print(args) 9 10 11 if __name__ == "__main__": 12 p = Pool(5) 13 for i in range(30): 14 p.apply_async(func=foo, args=(i,)) 15 p.close() # 等子进程执行完毕后关闭线程池 16 p.join()
12 协程
协程优点:
无需线程上下文切换的开销
无需原子操作锁定及同步的开销
方便切换控制流,简化编程模型
高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
协程缺点:
无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
1 # 协程的示例代码 2 import time 3 4 5 def consumer(): 6 r = "" 7 while True: 8 n = yield r 9 if not n: 10 return 11 print("消费者正在消费%s" % n) 12 time.sleep(1) 13 r = '200 ok' 14 15 16 def produce(c): 17 next(c) 18 n = 0 19 while n < 5: 20 n = n + 1 21 print("生产者正在生产%s" % n) 22 cr = c.send(n) 23 print("生产者消费反馈:%s" % cr) 24 c.close() 25 26 27 if __name__ == "__main__": 28 c = consumer() 29 produce(c)
greenlet
greenlet机制的主要思想是:生成器函数或者协程函数中的yield语句挂起函数的执行,直到稍后使用next()或send()操作进行恢复为止。可以使用一个调度器循环在一组生成器函数之间协作多个任务。greentlet是python中实现我们所谓的"Coroutine(协程)"的一个基础库。
1 # greenlet机制 2 from greenlet import greenlet 3 4 5 def test1(): 6 print(12) 7 gr2.switch() 8 print(34) 9 gr2.switch() 10 11 12 def test2(): 13 print(56) 14 gr1.switch() 15 print(78) 16 17 18 gr1 = greenlet(test1) 19 gr2 = greenlet(test2) 20 gr1.switch()
1 # 会用gevent.sleep()去切换协程,而是在执行到IO操作时,gevent自动切换 2 from gevent import monkey 3 monkey.patch_all() 4 import gevent 5 from urllib import request 6 import time 7 8 9 def func(url): 10 print('获取网址是:%s' % url) 11 resp = request.urlopen(url) 12 data = resp.read() 13 print("%d 字节从%s网址收到" % (len(data), url)) 14 15 16 start = time.time() 17 gevent.joinall( 18 [gevent.spawn(func, "https://itk.org"), 19 gevent.spawn(func, "https://www.github.com/"), 20 gevent.spawn(func, "https://zhihu.com/") 21 ] 22 ) 23 24 print(time.time()-start)
思路来源
http://www.cnblogs.com/yuanchenqi/articles/6755717.html
https://www.cnblogs.com/yuanchenqi/articles/5733873.html
浙公网安备 33010602011771号