进程是最小的资源单位,线程是最小的执行单位
一、进程
进程:就是一个程序在一个数据集上的一次动态执行过程。
进程由三部分组成:
1、程序:我们编写的程序用来描述进程要完成哪些功能以及如何完成
2、数据集:数据集则是程序在执行过程中所需要使用的资源
3、进程控制块:进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感
知进程存在的唯一标志。
二、线程
Threading用于提供线程相关的操作。线程是应用程序中工作的最小单元,它被包含在进程之中,是进程中的实际运作单位。一
条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
1、实现线程并发
示例1:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading #线程 6 import time 7 8 def Hi(num): #有一个参数 9 print("hello %s" %num)10 time.sleep(3) 11 12 if __name__ == '__main__':13 14 t1=threading.Thread(target=Hi,args=(10,)) #创建了一个线程对象t1,10做为一个参数,传给num15 t1.start()16 17 t2=threading.Thread(target=Hi,args=(9,)) #创建了一个线程对象t2,9做为一个参数,传给num18 t2.start()19 20 print("ending.........") #主线程输出ending
执行结果:
1 hello 10 #子线程2 hello 9 #子线程3 ending......... #主线程4 #上面三个同时出来,再停顿三秒才结束5 Process finished with exit code 0 #停顿3秒才结束
示例2:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 import time 7 8 def music(): 9 print("begin to listen %s"%time.ctime())10 time.sleep(3)11 print("stop to listen %s" %time.ctime())12 13 def game():14 print("begin to play game %s"%time.ctime())15 time.sleep(5)16 print("stop to play game %s" %time.ctime())17 18 if __name__ == '__main__':19 20 t1=threading.Thread(target=music)21 t1.start()22 t2=threading.Thread(target=game)23 t2.start()
执行结果:
1 #总共花了5秒时间2 3 begin to listen Sat Jan 14 12:34:43 20174 begin to play game Sat Jan 14 12:34:43 2017 #1、先打印2个5 6 stop to listen Sat Jan 14 12:34:46 2017 #2、等待3秒再打印一个7 8 stop to play game Sat Jan 14 12:34:48 2017 #3、再等待2秒,打印一个
2、使用join方法
示例1:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 import time 7 8 def music(): 9 print("begin to listen %s"%time.ctime())10 time.sleep(3)11 print("stop to listen %s" %time.ctime())12 13 def game():14 print("begin to play game %s"%time.ctime())15 time.sleep(5)16 print("stop to play game %s" %time.ctime())17 18 if __name__ == '__main__':19 20 t1=threading.Thread(target=music)21 t2=threading.Thread(target=game)22 23 t1.start() #运行实例的方法24 t2.start()25 26 t1.join() #子线程对象调用join()方法27 t2.join()28 29 print("ending") #在主线程中
执行结果:
begin to listen Sat Jan 14 12:58:34 2017begin to play game Sat Jan 14 12:58:34 2017 #先打印2个stop to listen Sat Jan 14 12:58:37 2017 #等待3秒,再打印一个stop to play game Sat Jan 14 12:58:39 2017 #等待2秒,再打印两个ending
示例2:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 import time 7 8 def music(): 9 print("begin to listen %s"%time.ctime())10 time.sleep(3)11 print("stop to listen %s" %time.ctime())12 13 def game():14 print("begin to play game %s"%time.ctime())15 time.sleep(5)16 print("stop to play game %s" %time.ctime())17 18 if __name__ == '__main__':19 20 t1=threading.Thread(target=music)21 t2=threading.Thread(target=game)22 23 t1.start() #运行实例的方法24 t2.start()25 26 t1.join() #t1线程不结束,谁都不往下走27 28 print("ending")
执行结果:
1 begin to listen Sat Jan 14 13:06:07 20172 begin to play game Sat Jan 14 13:06:07 2017 #先打印这两行3 4 stop to listen Sat Jan 14 13:06:10 2017 #再等待3秒打印这两行5 ending6 7 stop to play game Sat Jan 14 13:06:12 2017 #再等待2秒打印这行
示例3:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 import time 7 8 def music(): 9 print("begin to listen %s"%time.ctime())10 time.sleep(3)11 print("stop to listen %s" %time.ctime())12 13 def game():14 print("begin to play game %s"%time.ctime())15 time.sleep(5)16 print("stop to play game %s" %time.ctime())17 18 if __name__ == '__main__':19 20 t1=threading.Thread(target=music)21 t2=threading.Thread(target=game)22 23 t1.start() #运行实例的方法24 t2.start()25 26 t2.join()27 28 print("ending") #在主线程中
执行结果:
1 begin to listen Sat Jan 14 13:12:34 2017 #先打印这两行2 begin to play game Sat Jan 14 13:12:34 20173 4 stop to listen Sat Jan 14 13:12:37 2017 #等待3秒,打印这一行5 6 stop to play game Sat Jan 14 13:12:39 2017 #等待2秒,打印这两行7 ending
示例4:并没有实现并发(失去多线程的意义)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 import time 7 8 def music(): 9 print("begin to listen %s"%time.ctime())10 time.sleep(3)11 print("stop to listen %s" %time.ctime())12 13 def game():14 print("begin to play game %s"%time.ctime())15 time.sleep(5)16 print("stop to play game %s" %time.ctime())17 18 if __name__ == '__main__':19 20 t1=threading.Thread(target=music)21 t2=threading.Thread(target=game)22 23 t1.start()24 25 t1.join()26 t2.start()27 28 t2.join()29 30 print("ending") #在主线程中
执行结果:
1 begin to listen Sat Jan 14 13:26:18 2017 #先打印条1行2 3 stop to listen Sat Jan 14 13:26:21 2017 #等待3秒再打印2行4 begin to play game Sat Jan 14 13:26:21 20175 6 stop to play game Sat Jan 14 13:26:26 2017 #等待5秒打印2行7 ending
三、线程的两种调用方式
threading 模块建立在 thread 模块之上。thread 模块以低级、原始的方式来处理和控制线程,而 threading 模块通过对 thread
进行二次封装,提供了更方便的 api 来处理线程。
1、直接调用(推荐写法)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 import time 7 8 9 def sayhi(num): # 定义每个线程要运行的函数10 11 print("running on number:%s" % num)12 13 time.sleep(3)14 15 16 if __name__ == '__main__':17 t1 = threading.Thread(target=sayhi, args=(1,)) # 生成一个线程实例18 t2 = threading.Thread(target=sayhi, args=(2,)) # 生成另一个线程实例19 20 t1.start() # 启动线程21 t2.start() # 启动另一个线程22 23 print(t1.getName()) # 获取线程名24 print(t2.getName())
执行结果:
1 running on number:12 running on number:23 Thread-14 Thread-2
2、继承式调用(有些编程人员会用这种写法,也要能看懂。不推荐这种写法)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 import time 7 8 #自己定制一个MyThread的类 9 class MyThread(threading.Thread): 10 def __init__(self, num):11 threading.Thread.__init__(self)12 self.num = num13 14 def run(self): # 定义每个线程要运行的函数15 16 print("running on number:%s" % self.num)17 18 time.sleep(3)19 20 21 if __name__ == '__main__':22 t1 = MyThread(1) #继承这个类,把1这个参数,传给num ,t1就是个线程对象23 t2 = MyThread(2)24 t1.start()25 t2.start()26 27 print("ending......")
执行结果:
1 running on number:12 running on number:23 ending......
四、 threading.thread的实例方法
1、join&Daemon方法
示例1:没有用Daemon方法示例
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 from time import ctime,sleep 7 import time 8 9 def ListenMusic(name):10 11 print ("Begin listening to %s. %s" %(name,ctime()))12 sleep(3)13 print("end listening %s"%ctime())14 15 def RecordBlog(title):16 17 print ("Begin recording the %s! %s" %(title,ctime()))18 sleep(5)19 print('end recording %s'%ctime())20 21 #创建一个列表,把t1和t2加到列表中去22 threads = []23 t1 = threading.Thread(target=ListenMusic,args=('水手',))24 t2 = threading.Thread(target=RecordBlog,args=('python线程',))25 threads.append(t1)26 threads.append(t2)27 28 if __name__ == '__main__':29 30 for t in threads:31 t.start()32 33 print ("all over %s" %ctime())
执行结果:
1 Begin listening to 水手. Sat Jan 14 13:44:10 20172 Begin recording the python线程! Sat Jan 14 13:44:10 20173 all over Sat Jan 14 13:44:10 2017 #先打印三个出来; 主线程结束了4 5 end listening Sat Jan 14 13:44:13 2017 #等待3秒,打印这1个; 子线程还没有结束,会继续往下运行6 7 end recording Sat Jan 14 13:44:15 2017 #再等待2秒,打印这1个
示例2: 用Daemon方法示例(设置t为守护线程,就是子线程,跟着主线程一起退出)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 from time import ctime,sleep 7 import time 8 9 def ListenMusic(name):10 11 print ("Begin listening to %s. %s" %(name,ctime()))12 sleep(3)13 print("end listening %s"%ctime())14 15 def RecordBlog(title):16 17 print ("Begin recording the %s! %s" %(title,ctime()))18 sleep(5)19 print('end recording %s'%ctime())20 21 #创建一个列表,把t1和t2加到列表中去22 threads = []23 t1 = threading.Thread(target=ListenMusic,args=('水手',))24 t2 = threading.Thread(target=RecordBlog,args=('python线程',))25 threads.append(t1)26 threads.append(t2)27 28 if __name__ == '__main__':29 30 for t in threads:31 t.setDaemon(True) #设置t为守护线程; 注意:一定在start()之前设置,否则会报错32 33 t.start()34 35 print ("all over %s" %ctime())
执行结果:
1 Begin listening to 水手. Sat Jan 14 13:51:30 2017 #三个同时打印出来2 Begin recording the python线程! Sat Jan 14 13:51:30 20173 all over Sat Jan 14 13:51:30 2017
示例3:设置t1为守护线程,没有意义,达不到效果,因为t2还会继续执行
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 from time import ctime,sleep 7 import time 8 9 def ListenMusic(name):10 11 print ("Begin listening to %s. %s" %(name,ctime()))12 sleep(3)13 print("end listening %s"%ctime())14 15 def RecordBlog(title):16 17 print ("Begin recording the %s! %s" %(title,ctime()))18 sleep(5)19 print('end recording %s'%ctime())20 21 #创建一个列表,把t1和t2加到列表中去22 threads = []23 t1 = threading.Thread(target=ListenMusic,args=('水手',))24 t2 = threading.Thread(target=RecordBlog,args=('python线程',))25 threads.append(t1)26 threads.append(t2)27 28 if __name__ == '__main__':29 30 t1.setDaemon(True) #设置t1为守护线程; 注意:一定在start之前设置,否则会报错31 for t in threads:32 33 t.start()34 35 print ("all over %s" %ctime())
执行结果:
1 Begin listening to 水手. Sat Jan 14 14:02:07 20172 Begin recording the python线程! Sat Jan 14 14:02:07 20173 all over Sat Jan 14 14:02:07 2017 #设置t1为守护线程,所以会先把这三条先打印出来4 5 end listening Sat Jan 14 14:02:10 2017 #再等待3秒打印t2,6 7 end recording Sat Jan 14 14:02:12 2017 #再等待3秒打印这条出来
示例4:设置t2为守护线程,子线程才会跟着主线程一起退出
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 from time import ctime,sleep 7 import time 8 9 def ListenMusic(name):10 11 print ("Begin listening to %s. %s" %(name,ctime()))12 sleep(3)13 print("end listening %s"%ctime())14 15 def RecordBlog(title):16 17 print ("Begin recording the %s! %s" %(title,ctime()))18 sleep(5)19 print('end recording %s'%ctime())20 21 #创建一个列表,把t1和t2加到列表中去22 threads = []23 t1 = threading.Thread(target=ListenMusic,args=('水手',))24 t2 = threading.Thread(target=RecordBlog,args=('python线程',))25 threads.append(t1)26 threads.append(t2)27 28 if __name__ == '__main__':29 30 t2.setDaemon(True) # 设置t2为守护线程; 注意:一定在start之前设置,否则会报错31 for t in threads:32 33 t.start()34 35 print ("all over %s" %ctime())
执行结果:
1 Begin listening to 水手. Sat Jan 14 14:17:09 20172 Begin recording the python线程! Sat Jan 14 14:17:09 20173 all over Sat Jan 14 14:17:09 2017 #先打印这三条4 5 end listening Sat Jan 14 14:17:12 2017 #等待3秒,再打印这条;t1结束后,主线程也结束了。
2、一道面试题
1 #执行结果是什么? 2 3 i = 0 4 for i in range(10): 5 i += 1 6 print(i) 7 8 执行结果: 9 10
3、其它方法
示例:getName()方法 (一般没什么用)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 from time import ctime,sleep 7 import time 8 9 def ListenMusic(name):10 11 print ("Begin listening to %s. %s" %(name,ctime()))12 sleep(3)13 print("end listening %s"%ctime())14 15 def RecordBlog(title):16 17 print ("Begin recording the %s! %s" %(title,ctime()))18 sleep(5)19 print('end recording %s'%ctime())20 21 #创建一个列表,把t1和t2加到列表中去22 threads = []23 t1 = threading.Thread(target=ListenMusic,args=('水手',))24 t2 = threading.Thread(target=RecordBlog,args=('python线程',))25 threads.append(t1)26 threads.append(t2)27 28 if __name__ == '__main__':29 30 t2.setDaemon(True) # 设置t为守护进程; 注意:一定在start之前设置,否则会报错31 for t in threads:32 t.start()33 print(t.getName()) #返回线程名称:Thread-134 35 print ("all over %s" %ctime())
执行结果:
1 Begin listening to 水手. Sat Jan 14 14:36:44 20172 Thread-1 #返回线程名称3 Begin recording the python线程! Sat Jan 14 14:36:44 20174 Thread-2 #返回默认的线程名称5 all over Sat Jan 14 14:36:44 20176 end listening Sat Jan 14 14:36:47 2017
示例:threading.activeCount(),返回正在运行的线程数量
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 from time import ctime,sleep 7 import time 8 9 def ListenMusic(name):10 11 print ("Begin listening to %s. %s" %(name,ctime()))12 sleep(3)13 print("end listening %s"%ctime())14 15 def RecordBlog(title):16 17 print ("Begin recording the %s! %s" %(title,ctime()))18 sleep(5)19 print('end recording %s'%ctime())20 21 #创建一个列表,把t1和t2加到列表中去22 threads = []23 t1 = threading.Thread(target=ListenMusic,args=('水手',))24 t2 = threading.Thread(target=RecordBlog,args=('python线程',))25 threads.append(t1)26 threads.append(t2)27 28 if __name__ == '__main__':29 30 t2.setDaemon(True) #设置t为守护进程; 注意:一定在start之前设置,否则会报错31 for t in threads:32 t.start()33 34 print("count:", threading.active_count()) #判断有多少个线程的数量35 36 while threading.active_count()==1: #等于1就相当于只有一个主线程,没有子线程37 38 print ("all over %s" %ctime())
执行结果:
1 Begin listening to 水手. Sat Jan 14 14:49:00 20172 count: 23 Begin recording the python线程! Sat Jan 14 14:49:00 20174 count: 3 #得到的线程数量5 end listening Sat Jan 14 14:49:03 2017
五、进程与线程的关系区别
六、python的GIL
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features havegrown to depend on the guarantees that it enforces.)上面的核心意思就是:无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行。
常见概念:
1、什么是并发和并行?
并发:是指系统具有处理多个任务(动作)的能力(CPU通过切换来完成并发),并发是并行的一个子集。
并行:是指系统具有同时处理多个任务(动作)的能力
2、同步与异步的区别?
同步: 当进程执行到一个IO(等待外部数据)的时候你---->会一直等:同步 (示例: 打电话)
异步:当进程执行到一个IO(等待外部数据)的时候你---->不等:一直等到数据接收成功,再回来处理。异步效率更高(示例:发短信)
3、任务分为:
1、对于IO密集型的任务: python的多线程是有意义的,可以采用:多进程+协程的方式
2、对于计算密集型的任务:python的多线程就不推荐。python就不适用了。
七、同步锁
示例1:不加锁(拿到的值是不固定的)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 import time 7 8 def sub(): 9 global num10 11 # num -= 112 temp=num13 time.sleep(0.001) #别外75个线程,拿到100了,时间不固定。14 num=temp-115 16 num =10017 18 l=[]19 20 for i in range(100):21 t=threading.Thread(target=sub)22 t.start()23 l.append(t)24 25 for t in l:26 t.join()27 28 print(num)
执行结果:
1 73 or 75 (这个值是随机的,会不断变化)
示例2:加锁 (加锁的作用:就是把多线程变成串行,结果不会变)
1 #加锁的作用:就是把多线程变成串行,结果就不会变) 2 3 #!/usr/bin/env python 4 # -*- coding:utf-8 -*- 5 #Author: nulige 6 7 import threading 8 import time 9 10 def sub():11 12 global num13 14 # num -= 115 lock.acquire() #获取锁16 temp=num17 time.sleep(0.001) 18 num=temp-119 lock.release() #释放锁20 21 num =10022 23 l=[]24 lock=threading.Lock()25 26 for i in range(100):27 t=threading.Thread(target=sub)28 t.start()29 l.append(t)30 31 for t in l:32 t.join()33 34 print (num)
执行结果:
1 0
GIL:全局解释器锁
作用:保证同一时刻,只有一个线程被CPU执行,无论你有多少个线程。为什么这里还需要lock? 注意啦,这里的lock是用户级的lock,跟那个GIL没关系 ,具体我们通过下图进行讲解
既然用户程序已经自己有锁了,那为什么C python还需要GIL呢?加入GIL主要的原因是为了降低程序的开发的复杂度,比如现在的你写python不需要关心内存回收的问题,因为Python解释器帮你自动定期进行内存回收,你可以理解为python解释器里有一个独立的线程,每过一段时间它起wake up做一次全局轮询看看哪些内存数据是可以被清空的,此时你自己的程序 里的线程和 py解释器自己的线程是并发运行的,假设你的线程删除了一个变量,py解释器的垃圾回收线程在清空这个变量的过程中的clearing时刻,可能一个其它线程正好又重新给这个还没来及得清空的内存空间赋值了,结果就有可能新赋值的数据被删除了,为了解决类似的问题,python解释器简单粗暴的加了锁,即当一个线程运行时,其它人都不能动,这样就解决了上述的问题,这可以说是Python早期版本的遗留问题。
八、线程死锁和递归锁
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都
正在使用,所有这两个线程在无外力作用下将一直等待下去。
示例1:线程死锁
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author: nulige 4 5 import threading 6 import time 7 8 9 class MyThread(threading.Thread):10 def actionA(self):11 A.acquire() # count=112 print(self.name, "gotA", time.ctime())13 time.sleep(2)14 15 B.acquire() # count=216 print(self.name, "gotB", time.ctime())17 time.sleep(1)18 19 B.release() # count=120 A.release() # count=021 22 def actionB(self):23 B.acquire() # count=124 print(self.name, "gotB", time.ctime())25 time.sleep(2)26 27 A.acquire() # count=228 print(self.name, "gotA", time.ctime())29 time.sleep(1)30 31 A.release() # count=132 B.release() # count=033 34 def run(self):35 self.actionA()36 self.actionB()37 38 39 if __name__ == '__main__':40 41 A = threading.Lock()42 B = threading.Lock()43 44 L = []45 46 for i in range(5):47 t = MyThread()48 t.start()49 L.append(t)50 51 for i in L:52 i.join()53 54 print("ending.....")
执行结果:
1 Thread-1 gotA Mon Jan 16 17:33:58 20172 Thread-1 gotB Mon Jan 16 17:34:00 20173 Thread-1 gotB Mon Jan 16 17:34:01 20174 Thread-2 gotA Mon Jan 16 17:34:01 2017 #死锁,一直卡在这里
解决办法:
使用递归锁,将
1 2 | lockA = threading.Lock() lockB = threading.Lock()<br> #--------------<br>lock=threading.RLock() |
为了支持在同一线程中多次请求同一资源,python提供了“可重入锁”:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。
示例2:递归锁(解决死锁问题)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading 6 import time 7 8 class MyThread(threading.Thread): 9 10 def actionA(self):11 12 r_lcok.acquire() #count=113 print(self.name,"gotA",time.ctime())14 time.sleep(2)15 16 r_lcok.acquire() #count=217 print(self.name,"gotB",time.ctime())18 time.sleep(1)19 20 r_lcok.release() #count=121 r_lcok.release() #count=022 23 24 def actionB(self):25 26 r_lcok.acquire() #count=127 print(self.name,"gotB",time.ctime())28 time.sleep(2)29 30 r_lcok.acquire() #count=231 print(self.name,"gotA",time.ctime())32 time.sleep(1)33 34 r_lcok.release() #count=135 r_lcok.release() #count=036 37 def run(self):38 39 self.actionA()40 self.actionB()41 42 if __name__ == '__main__':43 44 r_lcok=threading.RLock()45 L=[]46 47 for i in range(5):48 t=MyThread()49 t.start()50 L.append(t)51 52 for i in L:53 i.join()54 55 print("ending.....")
执行结果:
1 Thread-1 gotA Mon Jan 16 17:38:42 2017 2 Thread-1 gotB Mon Jan 16 17:38:44 2017 3 Thread-1 gotB Mon Jan 16 17:38:45 2017 4 Thread-1 gotA Mon Jan 16 17:38:47 2017 5 Thread-3 gotA Mon Jan 16 17:38:48 2017 6 Thread-3 gotB Mon Jan 16 17:38:50 2017 7 Thread-4 gotA Mon Jan 16 17:38:51 2017 8 Thread-4 gotB Mon Jan 16 17:38:53 2017 9 Thread-5 gotA Mon Jan 16 17:38:54 201710 Thread-5 gotB Mon Jan 16 17:38:56 201711 Thread-5 gotB Mon Jan 16 17:38:57 201712 Thread-5 gotA Mon Jan 16 17:38:59 201713 Thread-3 gotB Mon Jan 16 17:39:00 201714 Thread-3 gotA Mon Jan 16 17:39:02 201715 Thread-4 gotB Mon Jan 16 17:39:03 201716 Thread-4 gotA Mon Jan 16 17:39:05 201717 Thread-2 gotA Mon Jan 16 17:39:06 201718 Thread-2 gotB Mon Jan 16 17:39:08 201719 Thread-2 gotB Mon Jan 16 17:39:09 201720 Thread-2 gotA Mon Jan 16 17:39:11 201721 ending.....
九、同步条件(Event)
An event is a simple synchronization object;the event represents an internal flag,and threads can wait for the flag to be set, or set or clear the flag themselves.event = threading.Event()# a client thread can wait for the flag to be setevent.wait()# a server thread can set or reset itevent.set()event.clear()If the flag is set, the wait method doesn’t do anything.If the flag is cleared, wait will block until it becomes set again.Any number of threads may wait for the same event.
示例:
1 import threading,time 2 class Boss(threading.Thread): 3 def run(self): 4 print("BOSS:今晚大家都要加班到22:00。") 5 print(event.isSet()) 6 event.set() 7 time.sleep(5) 8 print("BOSS:<22:00>可以下班了。") 9 print(event.isSet())10 event.set()11 class Worker(threading.Thread):12 def run(self):13 event.wait()14 print("Worker:哎……命苦啊!")15 time.sleep(1)16 event.clear()17 event.wait()18 print("Worker:OhYeah!")19 if __name__=="__main__":20 event=threading.Event()21 threads=[]22 for i in range(5):23 threads.append(Worker())24 threads.append(Boss())25 for t in threads:26 t.start()27 for t in threads:28 t.join()
执行结果:
1 BOSS:今晚大家都要加班到22:00。 2 False 3 Worker:哎……命苦啊! 4 Worker:哎……命苦啊! 5 Worker:哎……命苦啊! 6 Worker:哎……命苦啊! 7 Worker:哎……命苦啊! 8 BOSS:<22:00>可以下班了。 9 False10 Worker:OhYeah!11 Worker:OhYeah!12 Worker:OhYeah!13 Worker:OhYeah!14 Worker:OhYeah!15 ending.....
十、信号量(Semaphore)
信号量:指同时开几个线程并发
信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1。
计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)
BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import threading,time 6 7 class myThread(threading.Thread): 8 def run(self): #启动后,执行run方法 9 if semaphore.acquire(): #加把锁,可以放进去多个(相当于5把锁,5个钥匙,同时有5个线程)10 print(self.name)11 time.sleep(5)12 semaphore.release()13 14 if __name__=="__main__":15 semaphore=threading.Semaphore(5) #同时能有几个线程进去(设置为5就是一次5个线程进去),类似于停车厂一次能停几辆车16 17 thrs=[] #空列表18 for i in range(100): #100个线程19 thrs.append(myThread()) #加线程对象20 21 for t in thrs:22 t.start() #分别启动
执行结果:
1 Thread-1 2 Thread-2 3 Thread-3 4 Thread-4 5 Thread-5 #5个线程同时出来 6 7 Thread-8 8 Thread-6 9 Thread-910 Thread-711 Thread-10 #每隔3秒再打印5个出来12 13 部分省略.......
十一、多线程利器---队列(queue) ( 重点掌握)
列表是不安全的数据结构
示例:
1 #两个线程同时删除5,所以会报错,因为只有一个5,一个线程删除了就没有啦。 2 3 import threading,time 4 5 li=[1,2,3,4,5] 6 7 def pri(): 8 while li: 9 a=li[-1] #取值10 print(a)11 time.sleep(1)12 li.remove(a) #remove按索引去删除内容13 14 t1=threading.Thread(target=pri,args=()) #线程115 t1.start()16 t2=threading.Thread(target=pri,args=()) #线程217 t2.start()
执行结果:
1 #会报错,因为只有一个5,删除了就没有啦,不能两个线程同时删除。 2 3 5 4 5 5 4 6 Exception in thread Thread-2: 7 Traceback (most recent call last): 8 File "C:\Python3.5\lib\threading.py", line 914, in _bootstrap_inner 9 self.run()10 File "C:\Python3.5\lib\threading.py", line 862, in run11 self._target(*self._args, **self._kwargs)12 File "D:/python/day34/s10.py", line 34, in pri13 li.remove(a)14 ValueError: list.remove(x): x not in list15 16 317 218 1
思考:如何通过对列来完成上述功能?
queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.
queue列队的三种模式及构造函数
1、先进先出模式 (谁先进去,谁先出来) ---->class queue.Queue(maxsize)
2、先进后出模式 (先进去的,最后出来) ---->class queue.LifoQueue(maxsize)
3、优先级模式 (优先级越低,先出来) ---->class queue.PriorityQueue(maxsize)
一、先进先出
示例1:
1 #先进先出 (原则:谁先进去,谁就先出来) 2 import queue #线程 队列 3 4 q=queue.Queue() #先进先出 5 q.put(12) 6 q.put("hello") 7 q.put({ "name":"yuan"}) 8 9 while 1:10 data=q.get()11 print(data)12 print("-----------")
执行结果:
1 12 #他是第1个进去的,所以他先出来2 -----------3 hello4 -----------5 { 'name': 'yuan'}6 -----------
示例2:
1 import queue #队列,解决多线程问题 (注意:python2.7 Queue的首字母是大写的) 2 3 4 # q=queue.Queue(3) #1、设置3就是满了,默认(FIFO 先进先出 ) #先进后出(手枪弹夹,后压进去的,先出来) 5 q=queue.Queue() 6 q.put(12) 7 q.put("hello") 8 q.put({ "name":"yuan"}) 9 10 q.put(34)11 # q.put(34,False) #2、blook=True,如果改成Flase,提示你满了,会报错,但不会卡在这里12 13 14 while 1:15 data=q.get() #1、会卡着,等值进来16 # data=q.get(block=False) #3、队列为空17 print(data)18 print("-----------")
二、先进后出
示例:
1 #先进后出 2 import queue 3 4 q=queue.LifoQueue() #先进后出 5 6 q.put(12) 7 q.put("hello") 8 q.put({ "name":"yuan"}) 9 10 while 1:11 data=q.get() #卡着,等值进来,12 print(data)13 print("-----------")
执行结果:
1 { 'name': 'yuan'} #后进来的先出去2 -----------3 hello4 -----------5 126 -----------
三、优化级
示例:
1 #优先级 2 import queue 3 4 q=queue.PriorityQueue() #优先级 5 6 q.put([3,12]) 7 q.put([2,"hello"]) #2先出来,按优化级 级别是:2--->3--->4 从级到高 8 q.put([4,{ "name":"yuan"}]) 9 10 while 1:11 data=q.get()12 print(data[1])13 print("-----------------------")
执行结果:
1 hello #2先出来,按优先级2 -----------------------3 124 -----------------------5 { 'name': 'yuan'}6 -----------------------
queue队列类的方法:
创建一个“队列”对象import Queueq = Queue.Queue(maxsize = 10)Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。将一个值放入队列中q.put(10)调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。将一个值从队列中取出q.get()调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。此包中的常用方法(q = Queue.Queue()):q.qsize() 返回队列的大小q.empty() 如果队列为空,返回True,反之Falseq.full() 如果队列满了,返回True,反之Falseq.full 与 maxsize 大小对应q.get([block[, timeout]]) 获取队列,timeout等待时间q.get_nowait() 相当q.get(False)非阻塞 q.put(item) 写入队列,timeout等待时间q.put_nowait(item) 相当q.put(item, False)q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号q.join() 实际上意味着等到队列为空,再执行别的操作
示例1: q.qsize() and q.empty() and q.full
1 import queue 2 3 q=queue.Queue() 4 q.put(12) 5 q.put("hello") 6 q.put({ "name":"yuan"}) 7 8 print(q.qsize()) #判断队列大小 9 print(q.empty()) #判断队列是否为空10 print(q.full) #判断队列是否满了11 12 while 1:13 data=q.get()14 print(data)15 print("-----------")
执行结果:
3 --->q.qsize()False ---->q.empty()> --->full
示例2:g.put_nowait() 相当于q.get(Flase)
1 import queue 2 3 q=queue.Queue(3) 4 5 q.put(12) 6 q.put([2,"hello"]) 7 q.put([4,{ "name":"yuan"}]) 8 9 q.put_nowait(56) #相当于q.get(Flase)10 11 while 1:12 data=q.get()13 print(data)
执行结果:
1 Traceback (most recent call last):2 File "D:/python/day34/s7.py", line 79, in3 q.put_nowait(56) #相当于q.get(Flase)4 File "C:\Python3.5\lib\queue.py", line 184, in put_nowait5 return self.put(item, block=False)6 File "C:\Python3.5\lib\queue.py", line 130, in put7 raise Full8 queue.Full
十二、生产者消费者模型
1、为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
2、什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
这就像,在餐厅,厨师做好菜,不需要直接和客户交流,而是交给前台,而客户去饭菜也不需要不找厨师,直接去前台领取即可,这也是一个结耦的过程。
示例1:边做包子,边吃包子
1 #生产者消费者模型(生产者先执行,再吃包子。) 2 3 import time,random 4 import queue,threading 5 6 q = queue.Queue() 7 8 def Producer(name): 9 count = 010 while count <10:11 print("making........")12 time.sleep(random.randrange(3)) #产生一个随机数(1-2秒之间)13 q.put(count)14 print('Producer %s has produced %s baozi..' %(name, count))15 count +=116 print("ok......")17 18 def Consumer(name):19 count = 020 while count <10:21 time.sleep(random.randrange(4)) #产生一个随机数(1-3秒之间)22 if not q.empty():23 data = q.get()24 print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))25 else:26 print("-----no baozi anymore----")27 count +=128 29 p1 = threading.Thread(target=Producer, args=('A君',))30 c1 = threading.Thread(target=Consumer, args=('B君',))31 32 p1.start()33 c1.start()
执行结果:
1 making........ 2 Producer A君 has produced 0 baozi.. 3 ok...... 4 making........ 5 Consumer B君 has eat 0 baozi... 6 Producer A君 has produced 1 baozi.. 7 ok...... 8 making........ 9 Consumer B君 has eat 1 baozi...10 Producer A君 has produced 2 baozi..11 ok......12 making........13 Consumer B君 has eat 2 baozi...14 Producer A君 has produced 3 baozi..15 ok......16 making........17 Producer A君 has produced 4 baozi..18 ok......19 making........20 Consumer B君 has eat 3 baozi...21 Producer A君 has produced 5 baozi..22 ok......23 making........24 Producer A君 has produced 6 baozi..25 ok......26 making........27 Consumer B君 has eat 4 baozi...28 Producer A君 has produced 7 baozi..29 ok......30 making........31 Producer A君 has produced 8 baozi..32 ok......33 making........34 Producer A君 has produced 9 baozi..35 ok......36 Consumer B君 has eat 5 baozi...37 Consumer B君 has eat 6 baozi...38 Consumer B君 has eat 7 baozi...39 Consumer B君 has eat 8 baozi...40 Consumer B君 has eat 9 baozi...41 -----no baozi anymore----42 -----no baozi anymore----43 -----no baozi anymore----44 -----no baozi anymore----45 -----no baozi anymore----46 -----no baozi anymore----47 -----no baozi anymore----48 -----no baozi anymore----49 -----no baozi anymore----50 -----no baozi anymore----
示例2: 供不应求,吃包子的人太多了(1个人在生产包子,3个人在吃包子)
1 #生产者消费者模型(供不应求,吃的人太多了,生产不赢) 2 3 import time,random 4 import queue,threading 5 6 q = queue.Queue() 7 8 def Producer(name): 9 count = 010 while count <10:11 print("making........")12 time.sleep(random.randrange(3)) #产生一个随机数(1-2秒之间)13 q.put(count)14 print('Producer %s has produced %s baozi..' %(name, count))15 count +=116 print("ok......")17 18 def Consumer(name):19 count = 020 while count <10:21 time.sleep(random.randrange(4)) #产生一个随机数(1-3秒之间)22 if not q.empty():23 data = q.get()24 print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))25 else:26 print("-----no baozi anymore----")27 count +=128 29 p1 = threading.Thread(target=Producer, args=('A君',)) #1个人生产包子30 c1 = threading.Thread(target=Consumer, args=('B君',))31 c2 = threading.Thread(target=Consumer, args=('C君',)) #3个人在吃包子,导致吃包子的人太多啦,生产不赢32 c3 = threading.Thread(target=Consumer, args=('D君',))33 34 p1.start()35 c1.start()36 c2.start()37 c3.start()
执行结果:
1 making........ 2 -----no baozi anymore---- #生产不赢,供不应求,吃包子的人太多了 3 -----no baozi anymore---- 4 Producer A君 has produced 0 baozi.. 5 ok...... 6 making........ 7 Producer A君 has produced 1 baozi.. 8 ok...... 9 making........10 Consumer C君 has eat 0 baozi...11 Consumer D君 has eat 1 baozi...12 -----no baozi anymore----13 -----no baozi anymore----14 -----no baozi anymore----15 -----no baozi anymore----16 -----no baozi anymore----17 -----no baozi anymore----18 Producer A君 has produced 2 baozi..19 ok......20 making........21 Producer A君 has produced 3 baozi..22 ok......23 making........24 Producer A君 has produced 4 baozi..25 ok......26 making........27 Consumer C君 has eat 2 baozi...28 Consumer D君 has eat 3 baozi...29 Consumer D君 has eat 4 baozi...30 -----no baozi anymore----31 -----no baozi anymore----32 Producer A君 has produced 5 baozi..33 ok......34 making........35 Producer A君 has produced 6 baozi..36 ok......37 making........38 Producer A君 has produced 7 baozi..39 ok......40 making........41 Producer A君 has produced 8 baozi..42 ok......43 making........44 Consumer B君 has eat 5 baozi...45 Consumer C君 has eat 6 baozi...46 Consumer D君 has eat 7 baozi...47 Producer A君 has produced 9 baozi..48 ok......49 Consumer B君 has eat 8 baozi...50 Consumer C君 has eat 9 baozi...51 -----no baozi anymore----52 -----no baozi anymore----53 -----no baozi anymore----54 -----no baozi anymore----55 -----no baozi anymore----56 -----no baozi anymore----57 -----no baozi anymore----58 -----no baozi anymore----59 -----no baozi anymore----60 -----no baozi anymore----61 -----no baozi anymore----62 -----no baozi anymore----63 -----no baozi anymore----64 -----no baozi anymore----65 -----no baozi anymore----66 -----no baozi anymore----67 -----no baozi anymore----68 -----no baozi anymore----69 -----no baozi anymore----70 -----no baozi anymore----
示例3:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import time,random 6 import queue,threading 7 8 q = queue.Queue() 9 10 def Producer(name):11 count = 012 while count <10:13 print("making........")14 time.sleep(5)15 q.put(count)16 print('Producer %s has produced %s baozi..' %(name, count))17 count +=118 q.task_done() #发信号告诉队列在生产包子,让join接收,就开始吃包子19 print("ok......")20 21 def Consumer(name):22 count = 023 while count <10:24 time.sleep(random.randrange(4)) #产生一个随机数(1秒-3秒之间)25 print("waiting...等待包子做的过程中...")26 q.join() #join开始接收27 data = q.get()28 print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))29 count +=130 31 p1 = threading.Thread(target=Producer, args=('A君',))32 c1 = threading.Thread(target=Consumer, args=('B君',))33 c2 = threading.Thread(target=Consumer, args=('C君',))34 c3 = threading.Thread(target=Consumer, args=('D君',))35 36 p1.start()37 c1.start()38 c2.start()39 c3.start()
执行结果:
1 making........ 2 waiting...等待包子做的过程中... 3 waiting...等待包子做的过程中... 4 waiting...等待包子做的过程中... 5 Producer A君 has produced 0 baozi.. 6 ok...... 7 making........ 8 Consumer D君 has eat 0 baozi... 9 waiting...等待包子做的过程中...10 Producer A君 has produced 1 baozi..11 Consumer B君 has eat 1 baozi...12 waiting...等待包子做的过程中...13 ok......14 making........15 部分代码省略......
示例4:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 import time,random 6 import queue,threading 7 8 q = queue.Queue() 9 10 def Producer(name):11 count = 012 while count <10:13 print("making.....正在制作包子...")14 time.sleep(5)15 q.put(count)16 print('Producer %s has produced %s baozi..' %(name, count))17 count +=118 q.join()19 print("ok......")20 21 def Consumer(name):22 count = 023 while count <10:24 time.sleep(random.randrange(4)) #产生一个随机数(1秒-3秒之间)25 data = q.get()26 print("eating.......")27 time.sleep(4) #4秒钟这后28 q.task_done() #给他发一个信号,才打印ok29 print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))30 count +=131 32 p1 = threading.Thread(target=Producer, args=('A君',))33 c1 = threading.Thread(target=Consumer, args=('B君',))34 c2 = threading.Thread(target=Consumer, args=('C君',))35 c3 = threading.Thread(target=Consumer, args=('D君',))36 37 p1.start()38 c1.start()39 c2.start()40 c3.start()
执行结果:
1 making.....正在制作包子... 2 Producer A君 has produced 0 baozi.. 3 eating....... 4 Consumer B君 has eat 0 baozi... 5 ok...... 6 making.....正在制作包子... 7 Producer A君 has produced 1 baozi.. 8 eating....... 9 Consumer C君 has eat 1 baozi...10 ok......11 making.....正在制作包子...12 Producer A君 has produced 2 baozi..13 eating.......14 Consumer D君 has eat 2 baozi...15 ok......16 making.....正在制作包子...17 Producer A君 has produced 3 baozi..18 eating.......19 Consumer B君 has eat 3 baozi...20 ok......21 making.....正在制作包子...22 Producer A君 has produced 4 baozi..23 eating.......24 Consumer C君 has eat 4 baozi...25 ok......26 making.....正在制作包子...27 Producer A君 has produced 5 baozi..28 eating.......29 Consumer D君 has eat 5 baozi...30 ok......31 making.....正在制作包子...32 Producer A君 has produced 6 baozi..33 eating.......34 Consumer B君 has eat 6 baozi...35 ok......36 making.....正在制作包子...
总结:
task_done和join必须成对出现,类似于一个通信工具,我给你发个信号,你就知道我做了某个操作
(例如:put or get) 对方就是join。如果我put or get 你就处理。(类似于收到信号就处理)
类似于,我发信号,你收到就处理,没收到就Join卡住,一直在那等待。
十三、多进程模块
multiprocessing (主要解决GIL问题)
multiprocessing (主要解决GIL问题)
M
is a package that supports spawning processes using an API similar to the threading module. The ultiprocessing
package offers both local and remote concurrency,effectively side-stepping the by using subprocesses instead of threads. Due to this, the multiprocessing
module allows the programmer to fully leverage multiple processors on a given machine. It runs on both Unix and Windows.multiprocessing
由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。
multiprocessing包是Python中的多进程管理包。与threading.Thread类似,它可以利用multiprocessing.Process对象来创建一个进程。该进程可以运行在Python程序内部编写的函数。该Process对象与Thread对象的用法相同,也有start(), run(), join()的方法。此外multiprocessing包中也有Lock/Event/Semaphore/Condition类 (这些对象可以像多线程那样,通过参数传递给各个进程),用以同步进程,其用法与threading包中的同名类一致。所以,multiprocessing的很大一部份与threading使用同一套API,只不过换到了多进程的情景。
一、进程的调用
调用方式1:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 #多进程调用(并行) 6 7 from multiprocessing import Process 8 import time 9 10 11 def f(name):12 time.sleep(1)13 print('hello', name,time.ctime())14 15 if __name__ == '__main__':16 p_list=[]17 for i in range(3): #子进程18 19 p = Process(target=f, args=('alvin',))20 p_list.append(p)21 p.start()22 23 for i in p_list:24 i.join()25 26 print('end') #主进程
执行结果:
1 hello alvin Mon Jan 16 18:38:08 2017 #并行,三个同时出现2 hello alvin Mon Jan 16 18:38:08 20173 hello alvin Mon Jan 16 18:38:08 20174 end
调用方式2:
示例1: 1秒钟这后,4条消息同时执行
1 from multiprocessing import Process 2 import time 3 4 class MyProcess(Process): 5 6 def run(self): 7 time.sleep(1) 8 print ('hello', self.name,time.ctime()) 9 10 11 if __name__ == '__main__':12 p_list=[]13 for i in range(3):14 p = MyProcess() #进程对象15 p.start() #启动执行run方法16 p_list.append(p)17 18 for p in p_list:19 p.join() #子进程没有执行完,主进程会一直等待20 21 print('end')
执行结果:
1 hello MyProcess-1 Mon Jan 16 18:56:58 2017 #结果同时出来2 hello MyProcess-2 Mon Jan 16 18:56:58 20173 hello MyProcess-3 Mon Jan 16 18:56:58 20174 end
示例2:daemon=True 是属性,不是方法
1 #设置为守护进程,打印的就是end 2 3 from multiprocessing import Process 4 import time 5 6 class MyProcess(Process): 7 8 def run(self): 9 time.sleep(1)10 print ('hello', self.name,time.ctime())11 12 if __name__ == '__main__':13 p_list=[]14 15 for i in range(3):16 p = MyProcess() #进程对象17 p.daemon=True #是属性,不是方法18 p.start() #启动执行sun方法19 p_list.append(p)20 21 print('end') #主进程执行完之后,不管守护进程
执行结果:
1 end
调用方法3:
1 from multiprocessing import Process 2 import os 3 import time 4 5 def info(title): 6 print("title:", title) 7 print('parent process:', os.getppid()) #父进程的pid 8 print('process id:', os.getpid()) #打印进程号 9 10 def f(name):11 info('function f')12 print('hello', name)13 14 if __name__ == '__main__':15 info('main process line')16 17 time.sleep(1)18 print("------------------")19 p = Process(target=info, args=('yuan',))20 p.start()21 p.join()
执行结果:
1 title: main process line2 parent process: 42043 process id: 72804 ------------------5 title: yuan6 parent process: 72807 process id: 3596