python的进程模块

进程与线程

假如有两个程序A和B,程序A在执行到一半的过程中,需要读取大量的数据输入(I/O操作),而此时CPU只能静静地等待任务A读取完数据才能继续执行,这样就白白浪费了CPU资源。是不是在程序A读取数据的过程中,让程序B去执行,当程序A读取完数据之后,让程序B暂停,然后让程序A继续执行?当然没问题,但这里有一个关键词:切换。
切换,那么这就涉及到了状态的保存,状态的恢复,加上程序A与程序B所需要的系统资源(内存,硬盘,键盘等等)是不一样的。自然而然的就需要有一个东西去记录程序A和程序B分别需要什么资源,怎样去识别程序A和程序B等等,所以就有了一个叫进程的抽象。

进程定义:

进程就是一个程序在一个数据集上的一次动态执行过程。
进程一般由程序、数据集、进程控制块三部分组成。

举一例说明进程:
想象一位有一手好厨艺的计算机科学家正在为他的女儿烘制生日蛋糕。他有做生日蛋糕的食谱,厨房里有所需
的原料:面粉、鸡蛋、糖、香草汁等。在这个比喻中,做蛋糕的食谱就是程序(即用适当形式描述的算法)计算机科学家就是处理器(cpu),
而做蛋糕的各种原料就是输入数据。进程就是厨师阅读食谱、取来各种原料以及烘制蛋糕等一系列动作的总和。
现在假设计算机科学家的儿子哭着跑了进来,说他的头被一只蜜蜂蛰了。计算机科学家就记录下他
照着食谱做到哪儿了(保存进程的当前状态),然后拿出一本急救手册,按照其中的指示处理蛰伤。这
里,我们看到处理机从一个进程(做蛋糕)切换到另一个高优先级的进程(实施医疗救治),每个进程
拥有各自的程序(食谱和急救手册)。当蜜蜂蛰伤处理完之后,这位计算机科学家又回来做蛋糕,从他
离开时的那一步继续做下去。

线程定义

线程的出现是为了降低上下文切换的消耗,提高系统的并发性,并突破一个进程只能干一样事的缺陷,
使到进程内并发成为可能。

假设,一个文本程序,需要接受键盘输入,将内容显示在屏幕上,还需要保存信息到硬盘中。若只有
一个进程,势必造成同一时间只能干一样事的尴尬(当保存时,就不能通过键盘输入内容)。若有多
个进程,每个进程负责一个任务,进程A负责接收键盘输入的任务,进程B负责将内容显示在屏幕上的
任务,进程C负责保存内容到硬盘中的任务。这里进程A,B,C间的协作涉及到了进程通信问题,而且
有共同都需要拥有的东西-------文本内容,不停的切换造成性能上的损失。若有一种机制,可以使
任务A,B,C共享资源,这样上下文切换所需要保存和恢复的内容就少了,同时又可以减少通信所带
来的性能损耗,那就好了。是的,这种机制就是线程。

线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序
计数器、寄存器集合和堆栈共同组成。线程的引入减小了程序并发执行时的开销,提高了操作系统的并发
性能。线程没有自己的系统资源。

线程进程的关系区别

1.Threads share the address space of the process that created it; processes have their own address space.
线程共享创建它的进程的地址空间;进程们有它们各自的地址空间
2.Threads have direct access to the data segment of its process; processes have their own copy of the data segment of the parent process.
线程们可以直接访问它的进程数据;进程们有它们父进程的数据拷贝
3.Threads can directly communicate with other threads of its process; processes must use interprocess communication to communicate with sibling processes.
线程能够直接与其进程的其他线程交流;进程间的交流需要用交互进程才能与他们的近邻进程交流
4.New threads are easily created; new processes require duplication of the parent process.
新县城能容易创建;新进程需要复制父进程
5.Threads can exercise considerable control over threads of the same process; processes can only exercise control over child processes.
线程可以在同一进程的线程上执行大量操作;进程只能作业于子进程
6.Changes to the main thread (cancellation, priority change, etc.) may affect the behavior of the other threads of the process; changes to the parent process does not affect child processes.
对主线程的改变(取消,权限变更等...)可能会影响同一进程的其他线程的表现;对父进程的改变不会影响子进程

1.一个程序至少有一个进程,一个进程至少有一个线程.(进程可以理解成线程的容器)
2.进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
3.线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
4.进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
5.线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈)但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
6.一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.

python的GIL

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

上面的核心意思就是,无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行

python的线程和threading模块

线程调用的两种模式:直接调用、继承式调用

1.直接调用

import threading
import time

def speaksth(num):
    #定义函数用于线程运行
    print('start running threading%d' %num)
    time.sleep(num)

if __name__=='__main__':
    t1 = threading.Thread(target=speaksth,args=(1,))#生成一个线程实例
    t2 = threading.Thread(target=speaksth,args=(2,))#生成另一个线程实例
    t1.start()
    t2.start()
    print(t1.getName())#获取线程名
    print(t2.getName())  # 获取线程名
    print('end'.center(20,'-'))

2.继承式调用

import threading
import time

class m_thread(threading.Thread):
    def __init__(self,num,time):
        self.num = num
        self.time= time
        threading.Thread.__init__(self)

    def run(self):#定义每个线程的运行函数
        print('running on num:%s' %self.num)
        time.sleep(self.time)
        print('num:%s end'.center(20,'^') %self.num)

if __name__ == '__main__':
    t1 = m_thread(1,3)
    t2 = m_thread(2,5)
    t1.start()
    t2.start()
    print("main threading end".center(20,'-'))

3.threading.thread实例方法

.join()要加在线程.start()之后,先线程1.start()主线程一碰到 线程1.join()就停下来等待。

join() :

在子线程完成运行之前,这个子线程的父线程将一直被阻塞。在这个线程运行结束之前,其他线程等待。

情况1:
import threading
from time import ctime,sleep
import time

def ListenMusic(name):
    print("begin listening to %s. %s" %(name,ctime()))
    sleep(3)
    print("end listening %s"%ctime())
def RecordBlog(title):
    print("Begin recording the %s  %s" %(title,ctime()))
    sleep(5)
    print("end recording %s"%ctime())

threads = []
for i in range(5):
    threads.append(threading.Thread(target=ListenMusic,args=(str(i),)))

if __name__ == '__main__':
    for t in threads:
        t.start()
        t.join()
    print("End main thread %s"%ctime())

在这里插入图片描述
即,一个运行完再到另一个

情况2:
import threading
from time import ctime,sleep
import time
def ListenMusic(name):
    print("begin listening to %s. %s" %(name,ctime()))
    sleep(3)
    print("end listening %s"%ctime())
def RecordBlog(title):
    print("Begin recording the %s  %s" %(title,ctime()))
    sleep(5)
    print("end recording %s"%ctime())

threads = []
for i in range(5):
    threads.append(threading.Thread(target=ListenMusic,args=(str(i),)))

if __name__ == '__main__':
    for t in threads:
          t.start()
        #t.join()

    print("End main thread %s"%ctime())

在这里插入图片描述

Daemon() :守护线程

setDaemon(True):
将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。这个方法基本和join是相反的。 当我们在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以用setDaemon方法啦
例子:

import threading
from time import ctime,sleep
import time
def ListenMusic(name):
    print("begin listening to %s. %s" %(name,ctime()))
    sleep(3)
    print("end listening %s"%ctime())
def RecordBlog(title):
    print("Begin recording the %s  %s" %(title,ctime()))
    sleep(5)
    print("end recording %s"%ctime())
threads = []
for i in range(5):
    threads.append(threading.Thread(target=ListenMusic,args=(str(i),)))

if __name__ == '__main__':
    for t in threads:
        t.setDaemon(True) #一定要在start之前设置
        t.start()
    print("End main thread %s"%ctime())

在这里插入图片描述一瞬间执行完毕,不等子线程运行完成。

同步锁

import time
import threading
def addNum():
    global num #在每个线程中都获取这个全局变量
    #num-=1
    temp=num
    #print('--get num:',num )
    time.sleep(0.1)
    num =temp-1 #对此公共变量进行-1操作

num = 100  #设定一个共享变量
thread_list = []
for i in range(100):
    t = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)

for t in thread_list: #等待所有线程执行完毕
    t.join()

print('final num:', num )

在这里插入图片描述
改变time.sleep()的时间

import time
import threading
def addNum():
    global num #在每个线程中都获取这个全局变量
    #num-=1
    temp=num
    #print('--get num:',num )
    time.sleep(0.0001)
    num =temp-1 #对此公共变量进行-1操作

num = 100  #设定一个共享变量
thread_list = []
for i in range(100):
    t = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)

for t in thread_list: #等待所有线程执行完毕
    t.join()

print('final num:', num )

在这里插入图片描述
锁原理图:
在这里插入图片描述当time.sleep(0.1)变化时 /0.001/0.0000001
多个线程都在同时操作同一个共享资源,所以造成了资源破坏,怎么办呢?(join会造成串行,失去所线程的意义)

我们可以通过同步锁来解决这种问题

import time
import threading
R = threading.Lock()# 同步锁
def addNum():
    global num #在每个线程中都获取这个全局变量
    #num-=1
    R.acquire()#获取锁
    temp=num
    #print('--get num:',num )
    time.sleep(0.0001)
    num =temp-1 #对此公共变量进行-1操作
    R.release()#解锁

num = 100  #设定一个共享变量
thread_list = []
for i in range(100):
    t = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)

for t in thread_list: #等待所有线程执行完毕
    t.join()

print('final num:', num )

在这里插入图片描述

死锁和递归锁

事件event

An event is a simple synchronization object;the event represents an internal flag,

and threads can wait for the flag to be set, or set or clear the flag themselves.
event = threading.Event()

a client thread can wait for the flag to be set
event.wait()

a server thread can set or reset it
event.set()
event.clear()

If the flag is set, the wait method doesn’t do anything.
If the flag is cleared, wait will block until it becomes set again.
Any number of threads may wait for the same event.

import threading,time
class Boss(threading.Thread):
    def run(self):
        print('老板发话了吗?',event_ganhuo.isSet())# False
        time.sleep(1)
        print("BOSS:今晚大家都要加班到22:00。")
        event_ganhuo.set()
        print('老板发话了吗?',event_ganhuo.isSet())# True
        time.sleep(5)
        print('老板说下班了吗?',event_xiaban.isSet())# False
        time.sleep(1)
        print("BOSS:<22:00>可以下班了。")
        event_xiaban.set()
        print('老板说下班了吗?',event_xiaban.isSet())# True

class Worker(threading.Thread):
    def run(self):
        event_ganhuo.wait()#    一旦event被设定,等同于pass
        time.sleep(1)
        print("Worker:哎……命苦啊!")
        time.sleep(1)
        event_xiaban.wait()
        time.sleep(1)
        print("Worker:OhYeah!")

if __name__=="__main__":
    event_ganhuo=threading.Event()
    event_xiaban=threading.Event()

    threads=[]
    for i in range(5):
        threads.append(Worker())
    threads.append(Boss())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    print("ending.....")

运行结果:

在这里插入图片描述

信号量(控制同时可以运行的线程)

信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1。

import threading,time
class myThread(threading.Thread):
    def run(self):
        if semaphore.acquire():
            print(self.name)
            time.sleep(2)
            semaphore.release()
if __name__=="__main__":
    semaphore=threading.Semaphore(5)#每次能运行的线程
    thrs=[]
    for i in range(100):
        thrs.append(myThread())
    for t in thrs:
        t.start()

线程每隔2秒出现5个

队列——多线程利器

保证线程安全。
有线程队列也有进程队列

创建一个“队列”对象

import Queue
q = Queue.Queue(maxsize = 10)
Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。

将一个值放入队列中
q.put(10)
调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为
1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。

将一个值从队列中取出
q.get()
调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,
get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。

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

此包中的常用方法(q = Queue.Queue()):
q.qsize() 返回队列的大小
q.empty() 如果队列为空,返回True,反之False
q.full() 如果队列满了,返回True,反之False
q.full 与 maxsize 大小对应
q.get([block[, timeout]]) 获取队列,timeout等待时间
q.get_nowait() 相当q.get(False)
非阻塞 q.put(item) 写入队列,timeout等待时间
q.put_nowait(item) 相当q.put(item, False)
q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
q.join() 实际上意味着等到队列为空,再执行别的操作

import queue
import time
q = queue.Queue(3) #创建队列默认first in first out,只能存3个队列

q.put(12)
q.put('hello')
q.put({'name':'AFf'})
#q.put(34) #再加这一步会一直卡住,因为只能有3个
#q.put(34,False) #不会卡住,但是报错

while 1:
    data = q.get(block=False)#队列为空时,取不出则报错
    time.sleep(1)
    print(data)

在这里插入图片描述

优先级队列

import queue
import time
q = queue.PriorityQueue() #创建队列默认first in first out,只能存3个队列
q.put([3,12])
q.put([2,'hello'])
q.put([4,{'name':'AFf'}])
#q.put(34) #再加这一步会一直卡住,因为只能有3个
#q.put(34,False) #不会卡住,但是报错

while 1:
    data = q.get()#队列为空时,取不出则报错
    time.sleep(1)
    print(data)

在这里插入图片描述

生产者消费者模型

import queue,threading
import time
import random
q = queue.Queue()

def Producer(name):
    count = 0
    while count<5:
        print('begin produce bread')
        count += 1
        print('\033[32;1m%s has produced %d bread\033[0m'%(name,count))
        time.sleep(2)
        q.put(count)
        print('round end'.center(20,'-'))
        
def Consumer(name):
    data = 0
    while data<5:
        time.sleep(random.randint(1,2))
        if q.qsize()>0:
            print('begin eat bread'.center(20,'^'))
            data = q.get()
            print('%s has eat a bread')
        else:
            print("-----no baozi anymore----")

p1 = threading.Thread(target=Producer,args=('abc',))
c1 = threading.Thread(target=Consumer,args=('guest',))
p1.start()
c1.start()

多进程模块multiprocessing

Multiprocessing is a package that supports spawning processes using an API similar to the threading module. The multiprocessing package offers both local and remote concurrency,effectively side-stepping the Global Interpreter Lock by using subprocesses instead of threads. Due to this, the multiprocessing module allows the programmer to fully leverage multiple processors on a given machine. It runs on both Unix and Windows.

在这里插入图片描述

每秒钟运行3个

from multiprocessing import Process
import time
def f(name):
    time.sleep(1)
    print('hello', name,time.ctime())

if __name__ == '__main__':
    p_list=[]
    for i in range(3):
        for j in range(3):
            p = Process(target=f, args=('alvin',))
            p_list.append(p)
            p.start()

        for i in p_list:
            p.join()
    print('end')

show the individual process IDs involved

from multiprocessing import Process
import os
import time


def info(title):
    print("title:", title)
    print('parent process:', os.getppid())
    print('process id:', os.getpid())


def f(name):
    info('function f')
    print('hello', name)


if __name__ == '__main__':
    info('main process line')
    time.sleep(1)
    print("------------------")
    p = Process(target=info, args=('yuan',))
    p.start()
    p.join()

在这里插入图片描述

Process类

构造方法:
Process([group [, target [, name [, args [, kwargs]]]]])
  group: 线程组,目前还没有实现,库引用中提示必须是None;
  target: 要执行的方法;
  name: 进程名;
  args/kwargs: 要传入方法的参数。
实例方法:
  is_alive():返回进程是否在运行。
  join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。
  start():进程准备就绪,等待CPU调度
  run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。
  terminate():不管任务是否完成,立即停止工作进程
属性:
  daemon:和线程的setDeamon功能一样
  name:进程名字。
  pid:进程号。

import time
from multiprocessing import Process
class MyProcess(Process):
    def __init__(self,num):
        super(MyProcess,self).__init__()#继承父类的初始化字段
        self.num = num
    def run(self):
        time.sleep(1)
        print(self.pid)
        print(p.is_alive())
        print(self.num)
        time.sleep(1)

if __name__ == '__main__':
    p_list=[]
    for i in range(10):
        p = MyProcess(i)
        #p.daemon=True
        p_list.append(p)
    for p in p_list:
        p.start()
    print('main process end')

进程间通信

通过队列

import time #
#import queue #进程间的通信不能用线程队列
import multiprocessing
def foo(q):
    time.sleep(1)
    print("son process",id(q))#打印q的id
    q.put(123)
    q.put("yuan")
if __name__ == '__main__':
    #q=queue.Queue()
    q=multiprocessing.Queue()
    p=multiprocessing.Process(target=foo,args=(q,))#传一个进程队列q过去
    p.start()
    #p.join()
    print("main process",id(q))
    print(q.get())
    print(q.get())

在这里插入图片描述

通过管道

from multiprocessing import Process, Pipe
def f(conn):
    conn.send([12, {"name":"yuan"}, 'hello'])
    response=conn.recv()
    print("response",response)
    conn.close()
    print("q_ID2:",id(conn))

if __name__ == '__main__':

    parent_conn, child_conn = Pipe() #双向管道
    print("q_ID1:",id(child_conn))
    p = Process(target=f, args=(child_conn,))
    p.start()
    print(parent_conn.recv())   #父进程等待,如果没有收到将会阻塞, prints "[42, None, 'hello']"
    parent_conn.send("儿子你好!")
    p.join()

在这里插入图片描述

信息共享 manager

from multiprocessing import Process, Manager
def f(d, l,n):
    d[n] = '1'    #{0:"1"}
    d['2'] = 2    #{0:"1","2":2}
    l.append(n)    #[0,1,2,3,4,   0,1,2,3,4,5,6,7,8,9]
    #print(l)

if __name__ == '__main__':
    with Manager() as manager:
        d = manager.dict()#{}
        l = manager.list(range(5))#[0,1,2,3,4]
        p_list = []
        for i in range(10):
            p = Process(target=f, args=(d,l,i))
            p.start()
            p_list.append(p)
        for res in p_list:
            res.join()
        print(d)
        print(l)

在这里插入图片描述

参考链接:
http://www.cnblogs.com/yuanchenqi/articles/6248025.html

猜你喜欢

转载自blog.csdn.net/anny0001/article/details/84641125