zoukankan      html  css  js  c++  java
  • python开发之路之线程、进程、协程

    一、多进程和多线程

    共同点:

      让多个CPU同时处理请求

    区别:

      1.多线程中的线程在内存空间这一点上是共享的,进程与进程使用的是不同的内存空间。即创建线程不需要开辟内存空间,而创建新的进程需要为其分配新的内存空间

     

    全局解释器锁(GIL)

      在每一个进程的“出口”,是python特有的。它的作用是:做到了1个限制,什么限制呢,如果有2个线程同时被调度了,此时全局解释器锁就限制同时只能有1个穿过全局解释器锁,才能被CPU调度

     

    那什么时候该使用多进程,什么时候该使用多线程呢?

    • I/O密集型用多线程
    • 计算密集型用多进程

     

    线程对象的其它方法:

      • start            线程准备就绪,等待CPU调度
      • setName      为线程设置名称
      • getName      获取线程名称
      • setDaemon(True/False)   True是设置为后台线程(默认是设置的False,即不写是设置的前台线程)
                           如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止
                            如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
      • join              逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
      • run              线程被cpu调度后执行Thread类对象的run方法

    一、多线程示例1

    thread1.py

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    import threading
    import time
    
    def show(arg):
        time.sleep(1)
        print 'thread'+str(arg)
    
    for i in range(10):
        #创建1个线程,执行show方法,接收1个参数
        t = threading.Thread(target=show,args=(i,))
        
        #t.setDaemon(True)#主线程执行完成之后,就关闭
      #设置为前台线程,所有的线程执行完成后才关闭 
      t.setDaemon(False)
      t.start()
      
    print 'main thread stop'

     

    thread2.py

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    import threading
    import time
    
    def show(arg):
        time.sleep(1)
        print 'thread'+str(arg)
    
    for i in range(10):
        #创建1个线程,执行show方法,接收1个参数
        t = threading.Thread(target=show,args=(i,))
      #设置为后台线程,主线程执行完后就直接关闭
      t.setDaemon(True)
      t.start()


    print 'main thread stop'

     

     

    执行thread1.py,产生如下结果:

    执行thread2.py,产生如下结果:

     

     

    二、线程锁

      1.为什么要使用线程锁?

       python2.7默认每1个线程执行100条cpu指令。当执行多线程的时候关系到调用全局变量的时候,这时候就会因为多线程导致产生”脏数据“。那么,这时候我们就需要定义"线程锁"来避免产生脏数据,让一个线程执行完成之后,另外1个线程才能执行。即让多个线程按规则有序的执行,而不至于相互抢占着执行。下面是用和没用锁的2个例子:

     no_havelock.py

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    import threading
    import time
    
    gl_num = 0
    def show(arg):
        global gl_num
        time.sleep(1)
        gl_num+=1
        print gl_num
    
    
    for i in range(10):
        t = threading.Thread(target=show,args=[i,])
        t.start()
    
    print 'main thread stop'
    

     have_lock.py

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import threading
    import time
    
    gl_num = 0
    #定义全局解释器锁
    lock = threading.RLock()
    
    def show(arg):
        #从这里开始锁住
        lock.acquire()
        global gl_num
        time.sleep(1)
        gl_num+=1
        print gl_num
        #从这里开始释放锁
        lock.release()
    
    for i in range(10):
        t = threading.Thread(target=show,args=[i,])
        t.start()
    
    print 'main thread stop'
    

    结果:

       先执行 no_havelock.py,产生如下图示结果

       再执行 have_lock.py,产生如下图示结果

    三、事件event

      python线程中的事件,主要用于主线程控制其它线程的执行,即主线程能让子线程停下来,也能让子线程继续执行。event主要提供了3个方法,分别是:set(),wait(),clear().

      event实现的处理机制:

        1.全局定义了一个flag,

        2.如果flag = True,就让他继续执行。

        3.如果flag = False,就让它停止,等待着,直到flag = True,才继续往下执行。

     example.py

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import threading
    
    def do(event):
        print 'start'
        event.wait()  #判断flag,从而决定是继续执行还是挺住等待执行
        print 'execute'
    #定义1个事件
    event_obj = threading.Event()
    for i in range(10):
        t = threading.Thread(target=do,args=(event_obj,))
        t.start()
    
    #即将flag设置为False,即“红灯停”
    event_obj.clear()
    inp = raw_input("input:")
    if inp == 'true':
        #将flag设置为True,即“绿灯行”
        event_obj.set()
    

      

      然后执行example.py,执行结果如下:

      

      

        

    四、python的进程。创建多个进程,就可以同时利用多个CPU,创建进程的数量最好跟CPU数量相等。

      1.进程简单示例

      example.py

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from multiprocessing import Process
    #import threading
    import time
    
    def foo(i):
        print 'sys hello',i
    
    for i in range(3):
        p = Process(target=foo,args=(i,))
        p.start()
    

    这个实例必须在linux环境下,执行才能成功。

    2.如何实现进程数据共享?

      我们都知道,每个进程的内存空间资源是独立的,他默认是无法实现数据共享的,如果想要多个进程之间共享资源数据,那么该怎么处理呢?

      这时候就需要一个特殊的数据结构可以实现数据共享,使用Array定义特殊的数据结构:Array('i', [11,22,33,44])

      进程间数据共享定义方法1:

     

       可以把i替换成其它的如:

       'c': ctypes.c_char,  'u': ctypes.c_wchar,
        'b': ctypes.c_byte,  'B': ctypes.c_ubyte,
        'h': ctypes.c_short, 'H': ctypes.c_ushort,
        'i': ctypes.c_int,   'I': ctypes.c_uint,
        'l': ctypes.c_long,  'L': ctypes.c_ulong,
        'f': ctypes.c_float, 'd': ctypes.c_double

      共享数据示例1:
      
    from multiprocessing import Process,Array
    #通过Array创建1个只包含数字类型的的”数组“,即类似于python中的列表
    #并且数组的个数是不可变的,‘i’表示是数字类型的
    temp = Array('i', [11,22,33,44])
    
    
    def Foo(i):
        temp[i] = 100+i
        for item in temp:
            print i,'----->',item
    
    for i in range(2):
        p = Process(target=Foo,args=(i,))
        p.start()
    

      

      进程间数据共享定义方法2:

      

      共享数据示例2:

    #方法二:manage.dict()共享数据
    from multiprocessing import Process,Manager
    
    #定义Manager对象
    manage = Manager()
    #dic就是共享数据:字典
    dic = manage.dict()
    
    def Foo(i):
        dic[i] = 100+i
        print dic.values()
    
    for i in range(2):
        p = Process(target=Foo,args=(i,))
        p.start()
        p.join()
    

    执行流程: 

     

    五、python中的协程

      使用协程的目的:

        可以理解将线程“分片”,因为线程切换比较耗时。I/O操作比较多的话,最好采用协程比较合适。可以发多个I/O请求。  

    简单示例1:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from greenlet import greenlet
    
    def test1():
        print 12
        gr2.switch()  #切换到协程2
        print 34
        gr2.switch()  #切换到协程2
    
    
    def test2():
        print 56
        gr1.switch() #切换到协程1
        print 78
    
    gr1 = greenlet(test1)  #定义协程gr1
    gr2 = greenlet(test2)  #定义协程gr2
    gr1.switch()  #主动执行协程1
    

    整个示例的执行流程如下:

    简单示例2:

    from gevent import monkey; monkey.patch_all()
    import gevent
    import urllib2
    
    def f(url):
        print('GET: %s' % url)
        resp = urllib2.urlopen(url)
        data = resp.read()
        print('%d bytes received from %s.' % (len(data), url))
    
    gevent.joinall([
            gevent.spawn(f, 'https://www.python.org/'),
            gevent.spawn(f, 'https://www.yahoo.com/'),
            gevent.spawn(f, 'https://github.com/'),
    ])
    

      

    执行顺序图如下:

  • 相关阅读:
    【二分+字符串hs】[POI2000] 公共串
    【字符串匹配】【BKDRhash||KMP】
    【LCA】P4281 [AHOI2008]紧急集合 / 聚会
    【LCA专题】各种LCA求法
    【差分约束】POJ3159/LG P1993 小K的农场
    【差分约束】POJ1364/LG UVA515 king
    【差分约束】POJ1201/LG SP116 Intervals
    【差分约束】POJ3159 Candies
    【树形结构】LG P2052 [NOI2011]道路修建
    【拓扑排序+概率】LG P4316绿豆蛙的归宿
  • 原文地址:https://www.cnblogs.com/jachy/p/5107832.html
Copyright © 2011-2022 走看看