线程与进程

 

线程

  • 线程是计算机最小单位,与计算机交互, 它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流
  • 一个进程中可以并发多个线程,且每条纯种并行执行不同的任务
  • 线程之间共享内存地址
  • 多线程同时修改同一份数据时必须加锁,即互斥锁
  • 信号量(线程池,允许多少线程可以同时运行)
  • python多线程不适合cpu密集操作型任务,适合IO密集型的操作任务
  • IO操作是不占用CPU,计算会占用CPU
  • 守护线程,使用setDaemon(True)
  • 线程是同时运行的

信号量的实例 (线程池,允许多少线程可以同时运行)

import threading,time
 
def run(n):
    semaphore.acquire()
    time.sleep(1)
    print("run the thread: %s\n" %n)
    semaphore.release()
 
if __name__ == '__main__':
 
    num= 0
    semaphore  = threading.BoundedSemaphore(5) #最多允许5个线程同时运行
    for i in range(20):
        t = threading.Thread(target=run,args=(i,))
        t.start()
 
while threading.active_count() != 1:
    pass #print threading.active_count()
else:
    print('----all threads done---')
    print(num)
信号量

 生产消费者模型

import queue
import threading


def produce():
    for i in range(10):
        q.put('饺子%s' % i)
    print('等待饺子被取走...')
    q.join()
    print('饺子都被取完了...')


def consumer(n):
    while q.qsize() > 0:
        print('拿到', q.get())
        q.task_done()


q = queue.Queue()


p = threading.Thread(target=produce,)
p.start()

c = consumer('perple')
简单的生产消费者模型

 

定时器 Timer

def hello():
    print("hello, world")
 
t = Timer(10.0, hello)
t.start()  
# 会在10秒后执行hello函数

 

Events 事件驱动

通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则

import threading,time
import random


def light():
    if not event.isSet():
        event.set()   #wait就不阻塞 #绿灯状态
    count = 0
    while True:
        if count < 10:
            print('\033[42;1m--green light on---\033[0m')
        elif count <13:
            print('\033[43;1m--yellow light on---\033[0m')
        elif count <20:
            if event.isSet():
                event.clear()
            print('\033[41;1m--red light on---\033[0m')
        else:
            count = 0
            event.set()   #打开绿灯
        time.sleep(1)
        count +=1
def car(n):
    while 1:
        time.sleep(random.randrange(10))
        if  event.isSet():   #绿灯
            print("car [%s] is running.." % n)
        else:
            print("car [%s] is waiting for the red light.." %n)
if __name__ == '__main__':
    event = threading.Event()    # 创建event事件
    Light = threading.Thread(target=light)
    Light.start()
    for i in range(3):
        t = threading.Thread(target=car,args=(i,))
        t.start()
使用红绿灯例子实现Event的作用

 

进程 multiprocessing

  • 解决python使用多核cpu问题,使用进程
  • 语法与线程相似
  • 进程池

进程池

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

from multiprocessing import Process
import os

# 子进程要执行的代码
def run_proc(name):
    print('Run child process %s (%s)...' % (name, os.getpid()))

if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('Child process will start.')
    p.start()
    p.join()
    print('Child process end.')
multiprocessing例子
from multiprocessing import Process
import time
def f(name):
    time.sleep(2)
    print('hello', name)
 
if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()
进程例子

 

 

 

进程间数据交互之Manager方法

from multiprocessing import Process, Manager
 
def f(d, l):
    d[1] = '1'
    d['2'] = 2
    d[0.25] = None
    l.append(1)
    print(l)
 
if __name__ == '__main__':
    with Manager() as manager:
        d = manager.dict()
 
        l = manager.list(range(5))
        p_list = []
        for i in range(10):
            p = Process(target=f, args=(d, l))
            p.start()
            p_list.append(p)
     
        for res in p_list:    # 等待所有进程都执行完后再退出
            res.join()

  

 

队列 (queue)

 

协程 (gevent)

  Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

  • 协程是一种用户态的轻量级线程
  • 协程,又称微线程,纤程。英文名Coroutine
  • 协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:
  • 协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置

  协程的优点:

  • 无需线程上下文切换的开销
  • 无需原子操作锁定及同步的开销 
  • 方便切换控制流,简化编程模型

       所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。
    方便切换控制流,简化编程模型

  • 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理

  协程的缺点:

  • 无法利用多核资源:协程的本质是个单线程,它不能同时将单个CPU的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用
  • 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
from gevent import monkey; monkey.patch_socket()
import gevent

def f(n):
    for i in range(n):
        print gevent.getcurrent(), i

g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()
gevent栗子

 

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/'),
])
gevent IO切换

 

 

 

参考:https://softlns.github.io/2015/11/28/python-gevent/ 

 

posted on 2017-05-14 23:21  奋斗德路  阅读(122)  评论(0)    收藏  举报