• python进阶——进程/线程/协程


    1 python线程

      python中Threading模块用于提供线程相关的操作,线程是应用程序中执行的最小单元。

     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 
     4 import threading
     5 import time
     6   
     7 def show(arg):
     8     time.sleep(1)
     9     print 'thread'+str(arg)
    10   
    11 for i in range(10):
    12     #调用构造函数,实例化对象,第1个参数是线程执行的函数,第2个参数是函数参数
    13     t = threading.Thread(target=show, args=(i,))
    14     #线程准备就绪,等待CPU调度
    15     t.start()  
    16   
    17 print 'main thread stop'
    View Code

       上述代码创建了10个“前台”线程,然后控制器就交给了CPU,CPU根据指定算法进行调度,分片执行指令

       为什么是分片执行?

        python中有一个GIL(Global Interpreter Lock 全局解释器锁 ),即在同一时刻只有一个线程在执行,底层自动进行上下文切换。一个应用程序一般只存在一个进程,在进程中存在一个线程,线程是应用程序的最小执行单元。如果该进程中存在多个线程,其实也是串行执行的,即相当于在每个进程的出口,多个线程任务请求cpu调度,因为GIL的原因,只有一个线程能够被调度,所以单个进程不管有多少线程只能调度一个cpu。

    线程模块threading中的方法:

        start       线程准备就绪,等待CPU调度

        setName     为线程设置名称

        getName     获取线程名称

        setDaemon   设置为后台线程或前台线程(默认)           

                    如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止          

                    如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止

        join        逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义

        run         线程被cpu调度后执行Thread类对象的run方法

    2 线程锁

        由于线程是随机调度,可能某个线程只执行一部分代码,cpu就被调度执行其它线程了。下面是例子:

     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 
     4 import threading
     5 import time
     6 
     7 gl_num = 0
     8 
     9 def show(arg):
    10     global gl_num
    11     time.sleep(1)
    12     gl_num +=1
    13     print gl_num
    14 
    15 for i in range(10):
    16     t = threading.Thread(target=show, args=(i,))
    17     t.start()
    18 
    19 print 'main thread stop'
    没加线程锁代码

    执行结果

     1 #!/usr/bin/env python
     2 #coding:utf-8
     3    
     4 import threading
     5 import time
     6    
     7 gl_num = 0
     8    
     9 lock = threading.RLock() #实例化线程锁
    10    
    11 def Func():
    12     lock.acquire() #获取线程锁
    13     global gl_num
    14     gl_num +=1
    15     time.sleep(1)
    16     print gl_num
    17     lock.release() #释放线程锁,这里注意,在使用线程锁的时候不能把锁,写在代码中,否则会造成阻塞,看起来“像”单线程
    18        
    19 for i in range(10):
    20     t = threading.Thread(target=Func)
    21     t.start()
    加线程锁后代码

    执行结果

    3 event

        python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

    事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。

    • clear:将“Flag”设置为False
    • set:将“Flag”设置为True
     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3  
     4 import threading
     5  
     6 def do(event):
     7     print 'start'
     8     event.wait() #执行event对象wait方法,然后他们停下来,等待“Flag”为True
     9     print 'execute'
    10  
    11 event_obj = threading.Event() #创建事件的对象
    12 
    13 for i in range(10):
    14     t = threading.Thread(target=do, args=(event_obj,)) #event对象传给每个线程
    15     t.start()
    16  
    17 event_obj.clear()  #设置"Flag"为Flase
    18 
    19 inp = raw_input('input:')
    20 if inp == 'true':
    21     event_obj.set()
    event事件代码

    4 python进程

    1 from multiprocessing import Process
    2   
    3 def foo(i):
    4     print 'say hi',i
    5   
    6 for i in range(10):
    7     p = Process(target=foo,args=(i,))
    8     p.start()
    多进程代码

       注意:由于进程之间的数据需要各自持有一份,所以创建进程需要非常大的开销。并且python不能在Windows下创建进程!在使用多进程的时候,最好是创建和CPU核数相等进程数,默认进程之间相互独立,如果想让进程之间数据共享,就得有个特殊的数据结构,这个数据结构就可以理解为他有穿墙的功能。

       进程数据共享

        进程各自持有一份数据,默认无法共享数据

     1 #!/usr/bin/env python
     2 #coding:utf-8
     3  
     4 from multiprocessing import Process
     5 from multiprocessing import Manager
     6  
     7 import time
     8  
     9 li = []
    10  
    11 def foo(i):
    12     li.append(i)
    13     print 'say hi',li
    14   
    15 for i in range(10):
    16     p = Process(target=foo,args=(i,))
    17     p.start()
    18      
    19 print 'ending',li
    默认无法共享数据

        使用特殊的数据结构来实现进程共享数据

     1 默认进程之间是相互独立的,如果想让进程之间共享数据,那么在python中需要借助特殊的数据结构
     2 
     3 第1种方法:
     4 #通过特殊的数据结构Array
     5 
     6 from multiprocessing import Process
     7 from multiprocessing import Array
     8 
     9 #创建1个只包含数字类型的Array,可以看做是”类型与列表“组合的数据结构
    10 temp = Array('i', [11,22,33,44]) #其中i是数据类型,这里i表示整型,后面就是python中的列表
    11  
    12 def Foo(i):
    13     temp[i] = 100+i
    14     for item in temp:
    15         print i,'----->',item
    16  
    17 for i in range(2):
    18     p = Process(target=Foo,args=(i,))
    19     p.start()
    20     
    21 第二种方法:
    22 #通过特殊的数据结构manage.dict()共享数据
    23 
    24 from multiprocessing import Process
    25 from multiprocessing import Manager 
    26  
    27 #创建Manager对象manage
    28 manage = Manager()
    29 dic = manage.dict() #创建字典对象dic,这里的字典和python中字典使用方法一样!
    30  
    31 def Foo(i):
    32     dic[i] = 100+i
    33     print dic.values()
    34  
    35 for i in range(2):
    36     p = Process(target=Foo,args=(i,))
    37     p.start()
    38     p.join() #注意此处调用join方法,否则报错
    两种特殊数据结构实现进程间数据共享
     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 
     4 from multiprocessing import Process
     5 from multiprocessing import Array
     6 from multiprocessing import RLock
     7 
     8 def Foo(lock,temp,i):
     9     lock.acquire()  #获取锁
    10     temp[0] = 100+i
    11     for item in temp:
    12         print i,'--->',item
    13     lock.release()  #释放锁
    14 
    15 lock = RLock()  #生成锁对象
    16 temp = Array('i', [11, 22, 33, 44]) #用特殊数据结构Array实现进程间共享数据
    17 
    18 for i in range(20):
    19     p = Process(target=Foo,args=(lock,temp,i,))
    20     p.start()
    进程锁

    5 进程池

        进程池内部维护一个进程序列,当使用时去进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。

    进程池中有两个方法:apply 阻塞   apply_async 非阻塞

     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 
     4 from  multiprocessing import Process
     5 from  multiprocessing import Pool
     6 import time
     7   
     8 def Foo(i):
     9     time.sleep(2)
    10     return i+100
    11   
    12 def Bar(arg):
    13     print arg
    14   
    15 pool = Pool(5) #创建一个进程池
    16 #print pool.apply(Foo,(1,))#向进程池申请一个进程去执行Foo方法
    17 #print pool.apply_async(func =Foo, args=(1,)).get() #获取返回值
    18   
    19 for i in range(10):
    20     #Foo函数的执行结果返回,作为Bar函数的参数
    21     pool.apply_async(func=Foo, args=(i,),callback=Bar)
    22   
    23 print 'end'
    24 pool.close()
    25 pool.join()#进程池中进程执行完毕后终止程序,如果不调用jion方法,那么主进程执行完后程序直接关闭。
    进程池

    6 协程

          线程和进程都是由操作系统开辟的,即python进程和线程内部都是调用操作系统的API,这样系统的开销比较大。而对于协程,它是由程序员开辟的,由程序员进行控制,不需要调用操作系统的API。对于线程和进程来说,它是由CPU调度的,而协程完全由程序员控制,不需要CPU调度。
        协程的意义:对于多线程,CPU通过切片的方式来执行线程,线程切换时需要耗时(保存状态,下次继续)。协程则存在于线程中,在线程中控制代码块的执行顺序。
        适用场景:在其他语言中,协程存在的意义不大,因为多线程可以解决I/O操作问题,但是python存在GIL(Global Interpreter Lock 全局解释器锁 ),在同一时刻只能执行1个线程,所以,如果一个线程里I/O操作特别多,协程就比较适用,当进行IO操作时,可以调度执行其它代码段,避免由于IO操作时的阻塞导致CPU闲置

     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3  
     4 #导入协程模块greenlet
     5 from greenlet import greenlet  
     6  
     7 def test1():
     8     print 12
     9     gr2.switch() #切换到协程2
    10     print 34     #协程2切换回来之后,执行此语句,和yield类似
    11     gr2.switch() #切换到协程2
    12  
    13 def test2():
    14     print 56
    15     gr1.switch() #切换到协程1
    16     print 78
    17  
    18 gr1 = greenlet(test1)  #创建了一个协程对象
    19 gr2 = greenlet(test2)
    20 
    21 gr1.switch() #执行协程1 
    实例

      协程:把一个线程分成了多个协程,达到控制代码段的执行顺序,协程就是对线程的分片。上面的实例需要手动控制协程的执行顺序,因为greenlet需要人为的指定调度顺序的,而gevent对greenlet进行了封装,达到遇到IO操作自动切换。实例如下

     1 from gevent import monkey; monkey.patch_all()
     2 import gevent
     3 import urllib2
     4 
     5 def f(url):
     6     print('GET: %s' % url)
     7     resp = urllib2.urlopen(url) #当遇到I/O操作时,会调用协程操作,程序继续执行,协程就阻塞等待数据的返回
     8     data = resp.read()
     9     print('%d bytes received from %s.' % (len(data), url))
    10 
    11 gevent.joinall([
    12         gevent.spawn(f, 'https://www.python.org/'),  #f是调用的方法,其后的是传递的参数
    13         gevent.spawn(f, 'https://www.yahoo.com/'),
    14         gevent.spawn(f, 'https://github.com/'),
    gevent实例

    参考资料:

          http://www.cnblogs.com/wupeiqi/articles/5040827.html

          http://www.cnblogs.com/luotianshuai/p/5111587.html

          http://www.cnblogs.com/kaituorensheng/p/4445418.html(进程池)

  • 相关阅读:
    oracle导入dmp数据库文件
    Merge into的使用详解-你Merge了没有【转】
    远程调试
    安卓Activity、service是否处于同一进程
    AIDL机制实现进程间的通讯实例
    安卓android:scaleType属性
    oracle索引
    Json-lib用法
    浅谈position: absolute和position:relative
    Tab Layout教程
  • 原文地址:https://www.cnblogs.com/maociping/p/5132598.html
Copyright © 2020-2023  润新知