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/'),
    ])
    

      

    执行顺序图如下:

  • 相关阅读:
    Azure 虚拟机安全加固整理
    AzureARM 使用 powershell 扩容系统磁盘大小
    Azure Linux 云主机使用Root超级用户登录
    Open edX 配置 O365 SMTP
    powershell 根据错误GUID查寻错误详情
    azure 创建redhat镜像帮助
    Azure Powershell blob中指定的vhd创建虚拟机
    Azure Powershell 获取可用镜像 PublisherName,Offer,Skus,Version
    Power BI 连接到 Azure 账单,自动生成报表,可刷新
    Azure powershell 获取 vmSize 可用列表的命令
  • 原文地址:https://www.cnblogs.com/jachy/p/5107832.html
Copyright © 2011-2022 走看看