python进程和线程

一个任务就是一个进程,一个进程至少有一个线程。如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间

Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(父进程)复制了一份(子进程),然后,分别在父进程和子进程内返回。

子进程永远返回0,而父进程返回子进程的ID,这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程:

import os

print('Process (%s) start...' % os.getpid())
# Only works on Unix/Linux/Mac:
pid = os.fork()
if pid == 0:
    print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))
else:
    print('I (%s) just created a child process (%s).' % (os.getpid(), pid))

遗憾的是,由于Windows没有fork调用,上面的代码在Windows上无法运行,幸运的是,python提供了进程包multiprocessing

Process:创建子进程
lock: 多个进程访问共享资源时,避免访问冲突
Semaphore:用来控制对共享资源的访问数量
Event:实现进程间同步通信,如生产消费者模型(当一方不满足条件时,另一方应处于等待状态)
Queue:进程间通信
Pipe:进程间通信

由于GIL锁原因,Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。

1.Process

python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing,只需要定义一个函数,Python会完成其他所有事情.借助这个包,可以轻松完成从单进程到并发执行的转换。

创建函数并将其作为单个进程
p = multiprocessing.Process(target = worker, args = (3,))#创建进程
p.start()#启动进程

import multiprocessing
import time
 
def worker(interval):
    n = 5
    while n > 0:
        print("The time is {0}".format(time.ctime()))
        time.sleep(interval)
        n -= 1
 
if __name__ == "__main__":
    p = multiprocessing.Process(target = worker, args = (3,))#创建进程
    p.deamon = True #子进程设置了daemon属性,主进程结束(运行到最后一行p.is_alive)后,子进程也随着结束(p.start()不管运行完没,都结束。或为False(默认),则主进程结束后,p.start()子线程不会消失,自身运行完才消失)
    p.start()#启动进程
    p.join()#主线程处于等待状态,子线程p运行完后,主线程才继续往下运行
    print "p.pid:", p.pid
    print "p.name:", p.name
    print "p.is_alive:", p.is_alive()

创建函数并将其作为多个进程

import multiprocessing
import time

def worker_1(interval):
    print "worker_1"
    time.sleep(interval)
    print "end worker_1"

def worker_2(interval):
    print "worker_2"
    time.sleep(interval)
    print "end worker_2"

def worker_3(interval):
    print "worker_3"
    time.sleep(interval)
    print "end worker_3"

if __name__ == "__main__":
    p1 = multiprocessing.Process(target = worker_1, args = (2,))
    p2 = multiprocessing.Process(target = worker_2, args = (3,))
    p3 = multiprocessing.Process(target = worker_3, args = (4,))

    p1.start()
    p2.start()
    p3.start()

    print("The number of CPU is:" + str(multiprocessing.cpu_count()))
    for p in multiprocessing.active_children():
        print("child   p.name:" + p.name + "\tp.id" + str(p.pid))
    print "END!!!!!!!!!!!!!!!!!"

将进程定义为类

import multiprocessing
import time

class ClockProcess(multiprocessing.Process):
    def __init__(self, interval):
        multiprocessing.Process.__init__(self)
        self.interval = interval

    def run(self):
        n = 5
        while n > 0:
            print("the time is {0}".format(time.ctime()))
            time.sleep(self.interval)
            n -= 1

if __name__ == '__main__':
    p = ClockProcess(3)
    p.start()#进程调用start()时,自动调用run

join的作用:阻塞当前进程,直到调用join方法的那个进程执行完,再继续执行当前进程

2.Lock

当多个进程需要访问共享资源的时候,Lock可以用来避免访问的冲突

import multiprocessing
import sys

def worker_with(lock, f):
    with lock:
        fs = open(f, 'a+')
        n = 10
        while n > 1:
            fs.write("Lockd acquired via with\n")
            n -= 1
        fs.close()

def worker_no_with(lock, f):
    lock.acquire()
    try:
        fs = open(f, 'a+')
        n = 10
        while n > 1:
            fs.write("Lock acquired directly\n")
            n -= 1
        fs.close()
    finally:
        lock.release()

if __name__ == "__main__":
    lock = multiprocessing.Lock()
    f = "file.txt"
    w = multiprocessing.Process(target = worker_with, args=(lock, f))
    nw = multiprocessing.Process(target = worker_no_with, args=(lock, f))
    w.start()
    nw.start()
    print "end"
import multiprocessing
import sys

def worker_with(lock, f):
    with lock:
        fs = open(f, 'a+')
        n = 10
        while n > 1:
            fs.write("Lockd acquired via with\n")
            n -= 1
        fs.close()

def worker_no_with(lock, f):
    lock.acquire()
    try:
        fs = open(f, 'a+')
        n = 10
        while n > 1:
            fs.write("Lock acquired directly\n")
            n -= 1
        fs.close()
    finally:
        lock.release()

if __name__ == "__main__":
    lock = multiprocessing.Lock()
    f = "file.txt"
    w = multiprocessing.Process(target = worker_with, args=(lock, f))
    nw = multiprocessing.Process(target = worker_no_with, args=(lock, f))
    w.start()
    nw.start()
    print "end"
    

3.Semaphore用来控制对共享资源的访问数量,例如池的最大连接数

import multiprocessing
import time
 
def worker(s, i):
    s.acquire()
    print(multiprocessing.current_process().name + "acquire");
    time.sleep(i)
    print(multiprocessing.current_process().name + "release\n");
    s.release()
 
if __name__ == "__main__":
    s = multiprocessing.Semaphore(2)#池的最大连接数为2
    for i in range(5):
        p = multiprocessing.Process(target = worker, args=(s, i*2))
        p.start()

4.Event

Event用来实现进程间同步通信
在初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象,而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件,继续执行。

event.isSet(): 返回event的状态值True或者False;

event.wait(): 如果 event.isSet()==False将阻塞线程;

event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;

event.clear(): 恢复event的状态值为False。

import time,random
import queue,threading,multiprocessing

def producer(e,q):
    for i in range(5):
        e.clear()
        print("I'm try my hard")
        time.sleep(1)
        q.send(i)
        e.set()

def consumer(e,q):
    for i in range(5):
        e.wait()
        print(q.recv())




if __name__=='__main__':
    (pipe1,pipe2)=multiprocessing.Pipe()
    e=multiprocessing.Event()
    e.is_set()
    p1=multiprocessing.Process(target=producer,args=(e,pipe1))
    p2=multiprocessing.Process(target=consumer,args=(e,pipe2))
    p1.start()
    p2.start()



5.Queue

Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。

get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常。
q=Queue()#创建一无限长的队列;Queue(maxsize=10),最大长度为10
q.put(1)
print(q.get()) #get()方法从队头删除并返回一个项目。
Python queue模块有三种队列及构造函数:

1、Python queue模块的FIFO队列先进先出。 class queue.Queue(maxsize)
2、LIFO类似于堆栈,即先进后出。 class queue.LifoQueue(maxsize)
3、还有一种是优先级队列级别越低越先出来。 class queue.PriorityQueue(maxsize)

import queue
#先进后出

q=queue.LifoQueue()

q.put(34)
q.put(56)
q.put(12)

print(q.get())
print(q.get())
print(q.get())


#优先级
q=queue.PriorityQueue()
q.put([5,100])
q.put([7,200])
q.put([3,"hello"])
q.put([4,{"name":"alex"}])

while 1:
  data=q.get()
  print(data)

import multiprocessing


def writer_proc(q):
    try:
        q.put(1, block=False)
    except:
        pass


def reader_proc(q):
    try:
        print(q.get(block=False))
    except:
        pass


if __name__ == "__main__":
    q = multiprocessing.Queue()
    writer = multiprocessing.Process(target=writer_proc, args=(q,))
    reader = multiprocessing.Process(target=reader_proc, args=(q,))
    writer.start()
    writer.join()
    reader.start()

生产者消费者模型
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

这就像,在餐厅,厨师做好菜,不需要直接和客户交流,而是交给前台,而客户去饭菜也不需要不找厨师,直接去前台领取即可,这也是一个解耦的过程。

import time,random
import queue,threading,multiprocessing

def producer(e,q):
    for i in range(5):
        e.clear()
        print("I'm try my hard")
        time.sleep(1)
        q.send(i)
        e.set()

def consumer(e,q):
    for i in range(5):
        e.wait()
        print(q.recv())




if __name__=='__main__':
    (pipe1,pipe2)=multiprocessing.Pipe()
    e=multiprocessing.Event()
    e.is_set()
    p1=multiprocessing.Process(target=producer,args=(e,pipe1))
    p2=multiprocessing.Process(target=consumer,args=(e,pipe2))
    p1.start()
    p2.start()

6.Pipe

multiprocessing.Pipe([duplex])
返回2个连接对象(conn1, conn2),代表管道的两端,默认是双向通信.如果duplex=False,conn1只能用来接收消息,conn2只能用来发送消息.
(con1, con2) = Pipe()
con1.send([1,2,3])
print(con2.recv())

7.Pool

Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满, 那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来它

#coding: utf-8
import multiprocessing
import time
 
def func(msg):
    print( "msg:", msg)
    time.sleep(1)
    print ("end")
 
if __name__ == "__main__":
    pool = multiprocessing.Pool(processes = 3)
    for i in xrange(4):
        msg = "hello %d" %(i)
        pool.apply_async(func, (msg, ))   #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
 
    print ("Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~")
    pool.close()
    pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
    print ("Sub-process(es) done.")

apply_async(func[, args[, kwds[, callback]]]) 它是非阻塞,apply(func[, args[, kwds]]) 是阻塞的(理解区别,看例1例2结果区别)
close() 关闭pool,使其不在接受新的任务。
terminate() 结束工作进程,不在处理未完成的任务。
join() 主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用

from multiprocessing import Process, Value, Array

def f(n, a):
    n.value = 3.1415927
    for i in range(len(a)):
        a[i] = -a[i]

if __name__ == '__main__':
    num = Value('d', 0.0)
    arr = Array('i', range(10))

    p = Process(target=f, args=(num, arr))
    p.start()
    p.join()

    print(num.value)
    print(arr[:])
from multiprocessing import Process, Manager

def f(d, l):
    d[1] = '1'
    d['2'] = 2
    d[0.25] = None
    l.reverse()

if __name__ == '__main__':
    with Manager() as manager:
        d = manager.dict()
        l = manager.list(range(10))

        p = Process(target=f, args=(d, l))
        p.start()
        p.join()

        print(d)
        print(l)

线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。

协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;

#协程
import asyncio


async def cor1():
    print("COR1 start")
    await cor2()
    print("COR1 end")


async def cor2():
    print("COR2")


loop = asyncio.get_event_loop()#asyncio启动默认的event loop
loop.run_until_complete(cor1())#这个函数是阻塞执行的,知道所有的异步函数执行完成,
loop.close()

设计一个关于红绿灯的进程,5个关于车的进程;

对于车进程,每隔一个随机秒数,判断红绿灯的状态,是红灯或者黄灯,打印waiting;是绿灯打印running。

对于红绿灯进程: 首先默认是绿灯,做一个计数器,十秒前,每隔一秒打印“light green”;第十秒到第十三秒,每隔一秒打印“light yellow”,13秒到20秒, ‘light red’,20秒以后计数器清零。重新循环。

知识点:event对象(提示:event对象即红绿灯,为true是即绿灯,false时为黄灯或者红灯)

import multiprocessing,random,time

event=multiprocessing.Event()
def traffic_lights():
    count=0
    lights=['green light','yellow light','red light']
    current_light=lights[0]
    while True:
        while count<10:
            print(current_light,9-count)
            count+=1
            time.sleep(1)
        else:
            current_light=lights[1]
            event.set()

        while count<13:
            print(current_light,12-count)
            count+=1
            time.sleep(1)
        else:
            current_light=lights[2]

        while count<20:
            print(current_light,19-count)
            count += 1
            time.sleep(1)
            if count == 20:
                count=0
                current_light=lights[0]
                event.clear()
                break


def car(name):
    print(name,'starting...')
    while True:
        time.sleep(random.randint(1,4))
        if not event.is_set():
            print('%s is running'%name)
        else:
            print('%s is waiting'%name)

if __name__ == '__main__':
    t=multiprocessing.Process(target=traffic_lights)
    t.start()
    for i in range(5):
        c=multiprocessing.Process(target=car,args=('car%s'%(i+1),))
        c.start()


子线程里再创建进程,父线程就不卡
打包多进程程序,在main函数里添加 multiprocessing.freeze_support()
multiprocessing.log_to_stderr(logging.DEBUG)#多进程log

在界面程序,如QT中,创建子线程要在创建界面的时候(即一直存在,除非关闭应用)创建,然后用该子线程去调用计算密集型任务.(计算密集型里头用多进程加速)。释放主线程,保证界面不卡顿。利用pyqtSignal绑定一事件,使得子线程能够发送信号给主线程,用于更新界面。利用QTimer定时器,间隔获取信息
关闭线程,并不会关闭该线程创建的多个子进程。这时需手动关闭进程,用terminal

posted @ 2018-01-22 16:18  blog_hfg  阅读(134)  评论(0)    收藏  举报