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())
join()用法
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")
Process类直接调用
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()
队列Queues通信
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()
通道Pipe通信
进程间数据共享
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)
manager实现数据共享
进程池
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()
greenlet
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)