多线程

multiprocessing

如果你打算编写多进程的服务程序,python提供一个跨平台的多进程支持,multiprocessing模块就是跨平台版本的多进程模块。

multiprocessing模块提供了一个process类来代表一个进程对象

创建格式1:p = Process(target=函数名,args=( 函数的参数元组 ) )

启动格式:进程对象名.start()

多进程 Process

 1 #导入多进程模块
 2 from multiprocessing import Process
 3 #导入时间模块
 4 import time
 5 
 6 def sing(t,msg):
 7     #range(开始数值,结果数值),此函数是一组从开始数值到结果数值的数组
 8     for i in range(t,msg):
 9         print('我唱的第%d首歌'%i)
10 
11 def dace(t,msg):
12     for i in range(t,msg):
13         print('我跳的第%d支舞'%i)
14 
15 #__name__是当前程序的名称,__main__是运行到前台的名称,即如果当程程序运程到前台
16 if __name__=="__main__":
17     print('启动一个唱歌的进程')
18     # 创建进程
19     #创建格式:p = Process(target=函数名,args=( 函数的参数元组 ) )
20     p=Process(target=sing,args=(5,10))
21     p1=Process(target=dace,args=(5,10))
22     #延时2秒
23     time.sleep(2)
24     #进程对象名.start()
25     p.start()
26     p1.start()
27 '''
28 输出结果:
29 启动一个唱歌的进程
30 我唱的第5首歌
31 我唱的第6首歌
32 我唱的第7首歌
33 我唱的第8首歌
34 我唱的第9首歌
35 我跳的第5支舞
36 我跳的第6支舞
37 我跳的第7支舞
38 我跳的第8支舞
39 我跳的第9支舞
40 '''

 

创建格式2:p = Process(target=函数名,name='对象p的自定义名称',args=( 函数的参数元组 ),kwargs=函数的参数{ 字典 } )

 1 from multiprocessing import Process
 2 import time
 3 
 4 def download(g,g2,**kwargs):
 5     dice=kwargs['手机']
 6     if dice=='安卓':
 7         print('开始下载游戏%s,%s'%(g,g2))
 8         time.sleep(5)
 9         print('游戏下载完成')
10     else:
11         print('设备不匹配')
12 
13 if __name__=="__main__":
14     p=Process(target=download,name='pro',args=('跳一跳','跳二跳'),kwargs={'手机':'安卓'})
15     p.start()
16 
17     # time.sleep(5)
18     p.join(6) #阻塞 实现进程间的同步 ,等待子进程执行结束在继续往下执行
19     if p.is_alive()==True:
20         p.terminate() # 杀掉子进程
21     print('下载进程 %s 结束'%p.name)
22 '''
23 输出结果:
24 开始下载游戏跳一跳,跳二跳
25 游戏下载完成
26 下载进程 pro 结束
27 '''

 

调用系统多进程类,及主进程,父进程,子进程的认识

 1 #导入多线程模块
 2 from multiprocessing import Process
 3 #导入系统模块
 4 import os
 5 import time
 6 
 7 
 8 #创建一个类,它的父类是Process
 9 class ClassProcess(Process):
10     # 初始化
11     def __init__(self,num):
12         # 初始化父类的__init__魔术方法
13         Process.__init__(self)
14         #将传入的参数赋给父类Process的__init__,这样父类也就得到传入的参数
15         self.num=num
16 
17     def run(self):#run函数是固定函数,在p.start()执行后自动执行
18         #在进程的类执行os.getpid获取子进程,os.getppid()获取父进程
19         print('启动子进程%d,父进程%d'%(os.getpid(),os.getppid()),'参数是%s'%self.num)
20 
21 # 主进程的入口
22 if __name__=="__main__":
23     #在主程主执行os.getpid获取主进程
24     print('启动主进程%d'%os.getpid())
25     # 实例化自定义的类
26     p=ClassProcess('这是参数')
27     # 对于不包含target属性的Process类,执行start方法的时候会自动调用Process类中的run方法
28     p.start()
29     time.sleep(5)
30     p.join(3)
31     print('进程执行完毕')

 

实例:

 1 from multiprocessing import Process
 2 
 3 def sum(msg):
 4     j=0
 5     for i in range(1,msg+1):
 6         j+=i
 7     print('%d的累加和是%d'%(msg,j))
 8 
 9 if __name__=="__main__":
10     num=input('请输入一个整数:')
11     num=int(num)
12 
13     # 启动两个进程
14     p1=Process(target=sum,args=(num,))
15     # p2=ClassProcess(num)
16     p1.start()
17     #等待进程完成再继续执下下面程序
18     p1.join(10)
19     print('都计算完毕')

 

进程池:

当需要创建的子进程数量不多时,可以直接利用 multiprocessing  中的Process 动态生成多个进程,但是如果是上百个目标,手动的去创建进程工作量巨大,此时就可以使用 multiprocessing  模块提供的 Pool 方法。

初始化 Pool  时,可以指定一个最大进程数,当有新的请求提交到 Pool 中时,如果池还没有满,那么就会创建一个新的进程来执行请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池

中有进程结束,才会创建新的进程来执行。

 1 #导入进程池模块
 2 from multiprocessing import Pool
 3 import time
 4 #创建一个函数
 5 def dd(msg):
 6     s_start=time.time()
 7     print('开始执行进程%d'%msg)
 8     time.sleep(2)
 9     s_end=time.time()
10     print('进程%d执行结束'%msg)
11 if __name__=="__main__":
12  # 创建一个进程池 指定池子大小(最多能存放多少个进程)
13     p=Pool(3)
14     for i in range(6):
15         #进程池异步执行函数
16         # func=函数
17         p.apply_async(func=dd(i),args=(i,))#p.apply_async 异步执行
18     p.close() # 关闭进程池
19     p.join()

 

进程间的通讯-Queue

process之间有时需要进行通信,系统提供了很多机制来实现进程间的通信

初始化Queue() 对象时(例如:q=Queue()),若括号中没有指定最大可以接收的消息量,那么代表可接受无限

Queue.qsize():返回当前队列包含的消息数量,

Queue.empty():如果队列为空返回True,反之返回False。

Queue.full():如果队列清满了返回True,返之返回False。

Queue.put ( item , [ block [ , timeout ] ] ) :将item消息写入队列,block默认值为True

Queue.put_nowait():Queueput ( block = False)不等待写入消息

Queue.get ( [ block [ , timeout ] ] ) :获取队列中的一条消息,然后将其从列队中移除,block默认值为True。

Queue.get_nowait():是当于Queue.get ( block = False )不等待获取消息

 1 from multiprocessing import Queue
 2 
 3 if __name__=="__main__":
 4     # 实例化一个消息队列
 5     q=Queue(3)  # 队列最多只能存储三个消息
 6 #---------------------------------------------------------------------
 7     #存消息 q.put()
 8     q.put('消息1')
 9     q.put('消息2')
10     print(q.full())  # False 没有存满
11     #输出结果:False
12     q.put('消息3') # q.put('消息3',block=True,timeout=None)
13 # ---------------------------------------------------------------------
14     try:
15         # timeout=5等待5秒,5秒内block状态是True,5秒后是False(不等待直接加载消息)
16         q.put('消息4',block=True,timeout=5)
17     except:
18         print('消息队列满了')
19     # 输出结果:消息队列满了
20    # 判断消息队列是否存满 q.full() True 代表存满了
21     print(q.full())
22     #输出结果:True
23 # ---------------------------------------------------------------------
24 
25     # 判断有没有存满 如果没存满 就继续存
26     if not q.full():
27         q.put('消息5')
28     else:
29         print('已经满了,等待被人读取')
30     # 输出结果:已经满了,等待被人读取
31 # --------------------------------------------------------------------
32 
33     #q.get()读取消息,q.get_nowait()不等待读取消息
34     print(q.get())
35     # 输出结果:消息1
36     print(q.get_nowait())
37     # 输出结果:消息2
38     #不等待添加数据
39     if not q.full():
40         q.put_nowait('消息4')#q.put_nowait不等待存消息
41     #不等待读取
42     print(q.empty())  # 有东西的时候返回 False
43     # 输出结果:False
44     if not q.empty():
45         print(q.get_nowait())
46         # 输出结果:消息3

 Queue实例

 1 from multiprocessing import Queue,Process
 2 import time
 3 # 写的处理程序
 4 def write(q):
 5     for i in ['A','B','C']:
 6         print('开始写入%s'%i)
 7         q.put(i)
 8         time.sleep(2)
 9         print('%s写入成功'%i)
10 # 读的处理程序
11 def read(q):
12     while True:
13         if not q.empty():
14             for i in range(q.qsize()):
15                 #q.get()
16                 print('读出消息%s'%q.get())
17         else:
18             break
19 
20 if __name__=="__main__":
21     q=Queue()
22 
23     # 创建两个子进程
24     # 写的子进程
25     qw=Process(target=write(q),args=(q,))
26     qw.start()
27     qw.join()
28     #  读的子进程
29     qr=Process(target=read(q),args=(q,))
30     qr.start()
31     qr.join()
32     
33     '''
34     输出结果:
35     开始写入A
36     A写入成功
37     开始写入B
38     B写入成功
39     开始写入C
40     C写入成功
41     读出消息A
42     读出消息B
43     读出消息C
44     '''

 

多线程-threading

多任务可以由多进程完成,也可以由一个进程内的多线程完成。

我们前面提到了进程是由若干个线程组成的,一个进程至少有一个线程

多线程的使用:启动一个线程就是把一个函数传入并创建 Thread 实例,然后调用 start() 开始执行

1.

 1 import time
 2 import threading
 3 
 4 def download(i):
 5     print('开始现在文件%d'%i)
 6     time.sleep(i*2)
 7     print('文件%d下载完成'%i)
 8 
 9 
10 if __name__=="__main__":
11     # 单线程执行
12     # for i in range(5):
13     #     download(i)
14 
15     # 多线程
16     for i in range(4):
17         t=threading.Thread(target=download,args=(i,))
18         # 启动线程
19         t.start()
20 
21     while len(threading.enumerate()) > 1 : #因为进程一但启动最少有一个主线程,所以总线程最少是1
22         length=len(threading.enumerate())#threading.enumerate()当前运行的线程总数
23         #每秒检测一下系统运程的总线程数
24         print(length)
25         time.sleep(1)
26 '''
27 输出结果:
28 开始现在文件0
29 文件0下载完成
30 开始现在文件1
31 开始现在文件2
32 开始现在文件34
33 
34 4
35 文件1下载完成
36 3
37 3
38 文件2下载完成
39 2
40 2
41 文件3下载完成
42 '''

2.

 1 # 使用继承自定义线程
 2 import threading
 3 import time
 4 
 5 #创建一个类,继承了系统的线程类
 6 class MyThread(threading.Thread):
 7     #重载系统Thread类的__init__方法并加入参数num
 8     def __init__(self,num):
 9         super().__init__()
10         self.num=num
11     #系统Thread类的run方法是在start()后自动运行
12     def run(self):
13         time.sleep(2)
14         print('%d线程启动了'%self.num)
15 
16 
17 if __name__=="__main__":
18     # 实例化自定义类
19     t=MyThread(1)
20     # 启动线程对象
21     t.start()
22     time.sleep(1)
23     print('这是主线程')

3.多线程同时并发的实例展现

 1 import threading,time,random
 2 #在多线程类里面,每个线程都是同时运行的
 3 class MyThread(threading.Thread):#继承父类的类
 4     def __init__(self,num):
 5         #重载父类的__init__方法
 6         threading.Thread.__init__(self)
 7         #将 num 参数传入父类,num参数将是到时循环 i 的值
 8         self.num=num
 9     def run(self):#run方法在线程start()开启时自动运行
10         #获取一个随机时间
11         tim = random.random()
12         #延迟载入随机时间
13         time.sleep(tim)
14         #输出子线程的内容和子线程运行的时间
15         print('输出的随机时间是:' + str(tim))
16         print('我是的mnae是%s,我的num参数是%s'%(self.name,self.num))
17 if __name__=="__main__":
18     #循环一个5次的数,将此循环5次的都利用多线程同时输出
19     for i in range(5):
20         #实例化多线程类,将每个循环赋给多线程运行
21         #重点:此多线程同时运行,但因延时的时同不相同,延迟久的将慢输出
22         t=MyThread(i)
23         #开始多线程运行
24         t.start()
25 '''
26 输出结果:注:由于是同时并发,所在随机时间是从小到大
27 输出的随机时间是:0.07945692195157339
28 我是的mnae是Thread-1,我的num参数是0    注:num就是i的值
29 输出的随机时间是:0.23055532509566168
30 我是的mnae是Thread-4,我的num参数是3    注:此时num的值应该是2,但由于多线程是同时并发,此时
31                                            此第3个循环的延迟时间比第2个循环的延迟快,所以在此出现
32 输出的随机时间是:0.24012970090038033
33 我是的mnae是Thread-5,我的num参数是4
34 输出的随机时间是:0.5786985908120015
35 我是的mnae是Thread-3,我的num参数是2
36 输出的随机时间是:0.6175918079426753
37 我是的mnae是Thread-2,我的num参数是1
38 '''

4.关于子线程运程的结果响影主进程的变量的展现

 1 import threading
 2 g_num=1
 3 
 4 def action1():
 5     #global 全局变量声明
 6     global g_num
 7     g_num+=1
 8     print('g_num变量在子线程的值=%d\n'%g_num)
 9 
10 if __name__=="__main__":
11     #将函数引入线程运行,它的结果还是会影响到主进程
12     t1=threading.Thread(target=action1)
13     t1.start()
14     print('g_num变量在主进程的值=%d'%g_num)

 5.多线程的参数传入和并发的结果展现

 1 import threading,time
 2 
 3 def work1(msg):
 4     print('这是work1线程,参数为%s'%msg)
 5     time.sleep(2)
 6     msg.append(44)
 7     print('这是work1线程,改变后的参数为%s' % msg)
 8 
 9 def work2(msg):
10     print('这是work2线程,参数为%s'%msg)
11 
12 if __name__=="__main__":
13     list1=[11,22,33]
14     t1=threading.Thread(target=work1,args=(list1,))
15     t1.start()
16 #输出结果:这是work1线程,参数为[11, 22, 33]
17 #输出结果:这是work1线程, 改变后的参数为[11, 22, 33, 44]
18     t2=threading.Thread(target=work2,args=(list1,))
19     t2.start()
20 # 输出结果:这是work2线程,参数为[11, 22, 33]
21 
22     #线程1,线程2,运行完成后才继续执下主进程下面的程序
23     t1.join()
24     t2.join()
25 
26     #主进程输出列表的值
27     print(list1)
28 # 输出结果:[11, 22, 33, 44]
29 
30 '''
31 输出结果:
32 这是work1线程,参数为[11, 22, 33]
33 这是work2线程,参数为[11, 22, 33]
34 这是work1线程,改变后的参数为[11, 22, 33, 44]  注:这个输出本来是在第二项,但因延时,所以比线程2慢输出
35 [11, 22, 33, 44]
36 '''

6.协同线程同步

1.多线程遇到的数据混乱问题

 1 import threading,time
 2 num=0
 3 
 4 def work1():
 5     global num
 6     for i in range(10000000):
 7         num+=1
 8     print('----work1---num=%d' % num)
 9 def work2():
10     global num
11     for i in range(10000000):
12         num+=1
13     print('----work2---num=%d\n'%num)
14 if __name__=="__main__":
15 
16     t1=threading.Thread(target=work1)
17     t2=threading.Thread(target=work2)
18     t1.start()
19     t1.join()
20     t2.start()
21     t2.join()
22     # time.sleep(5)
23     print(num)
24 '''
25 输出结果:下面结果,进程中所有线程是同时进行,但函数1,和函数2在计算累加过程需要时间,而主进程不会等待
26           在主进程的 print(num)的需出结果是在函数1和函数2运算中间就得出结果 172483 ,需且这个数是不确
27           定的,由于全局变量 num 的不确定性导致函数1和函数2的累加结果也成为不确定数
28 172483
29 ----work1---num=11693771
30 ----work2---num=11722647
31 '''
32 '''
33 输出结果:加入t1.join()和t2.join()后,结果准确了,但这样线程不是同时进行,而是串行,和不运行线程的程序无异
34 ----work1---num=10000000
35 ----work2---num=20000000
36 20000000
37 '''

2.解决问题的思路

对于本小节提出的计算错误的问题,可以通过线程同步来进行解决,思路:如下:系统调用1,然后获取到 num 的值为0 ,此时上一把锁,即不允许其他线程操作 num ,对 num 的值进

行 +1 解锁,此时 num 的值为1,其他的线程可以使用 num了,而且是 num 的值不是 0 而是 1 ,则理其他线程在对 num 进行修改时,都要先上锁,处理完后解锁,在上锁的整个过程中

不允许其他线程访问,就保证了数据的正确性

threading 模块中定义了 Lock 类,可以方便处理锁定:

创建锁:nutex = threading.Lock()

上锁:   mutex.acquire()

开锁:   mutex.release()

 1 import threading,time
 2 num=0
 3 
 4 def work1():
 5     global num
 6     #--------1.定义一个锁,这个锁的初始状态为 True----------------------------------------
 7     t1_F=mutex.acquire(True) # 返回一个状态值
 8     for i in range(10000000):
 9         # --------2上锁:   mutex.acquire()
10         if t1_F:
11            num+=1
12     print('----work1---num=%d' % num)
13     # --------3开锁:   nutex.release()
14     mutex.release()  # 解锁
15 
16 def work2():
17     global num
18     # --------1.定义一个锁,这个锁的初始状态为 True----------------------------------------
19     t2_F = mutex.acquire(True)
20     for i in range(10000000):
21         # --------2上锁:   mutex.acquire()
22         if t2_F:
23            num+=1
24     print('----work2---num=%d\n'%num)
25     # --------3开锁:   nutex.release()
26     mutex.release()  #解锁
27 
28 if __name__=="__main__":
29     # 创建锁:mutex = threading.Lock()
30     mutex=threading.Lock()
31     t1=threading.Thread(target=work1)
32     t2=threading.Thread(target=work2)
33     t1.start()
34     #t1.join()
35     t2.start()
36     #t2.join()
37     # time.sleep(5)
38     print(num)
39 '''
40 输出结果:注:这样又能同步进行,函数时的结果就不会混乱,主进程在两个函数累加的过程中得到结果不会影响到
41               函数同部的结果
42 215481
43 ----work1---num=10000000
44 ----work2---num=20000000
45 '''

 

posted @ 2019-05-24 12:53  双伟科技  阅读(305)  评论(0)    收藏  举报