Python之进程与线程

一.进程

1.什么是进程

程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。

在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。

2.有了进程为什么还要线程?

进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:

  • 进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
  • 进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

例如,我们在使用qq聊天, qq做为一个独立进程如果同一时间只能干一件事,那他如何实现在同一时刻 即能监听键盘输入、又能监听其它人给你发的消息、同时还能把别人发的消息显示在屏幕上呢?你会说,操作系统不是有分时么?但我的亲,分时是指在不同进程间的分时呀, 即操作系统处理一会你的qq任务,又切换到word文档任务上了,每个cpu时间片分给你的qq程序时,你的qq还是只能同时干一件事呀。

再直白一点, 一个操作系统就像是一个工厂,工厂里面有很多个生产车间,不同的车间生产不同的产品,每个车间就相当于一个进程,且你的工厂又穷,供电不足,同一时间只能给一个车间供电,为了能让所有车间都能同时生产,你的工厂的电工只能给不同的车间分时供电,但是轮到你的qq车间时,发现只有一个干活的工人,结果生产效率极低,为了解决这个问题,应该怎么办呢?。。。。没错,你肯定想到了,就是多加几个工人,让几个人工人并行工作,这每个工人,就是线程!

3.

二.线程

1.什么是线程

  在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程

  多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间

  进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位

例如,北京地铁与上海地铁是不同的进程,而北京地铁里的13号线是一个线程,北京地铁所有的线路共享北京地铁所有的资源,比如所有的乘客可以被所有线路拉。

2.为什么要用线程

  多线程指的是,在一个进程中开启多个线程,简单的讲:如果多个任务共用一块地址空间,那么必须在一个进程内开启多个线程。详细的讲分为4点: 

  1. 多线程共享一个进程的地址空间

  2. 线程比进程更轻量级,线程比进程更容易创建可撤销,在许多操作系统中,创建一个线程比创建一个进程要快10-100倍,在有大量线程需要动态和快速修改时,这一特性很有用

  3. 若多个线程都是cpu密集型的,那么并不能获得性能上的增强,但是如果存在大量的计算和大量的I/O处理,拥有多个线程允许这些活动彼此重叠运行,从而会加快程序执行的速度。

  4. 在多cpu系统中,为了最大限度的利用多核,可以开启多个线程(比开进程开销要小的多)

三.Python并发编程之多进程

1.multiprocessing模块介绍

  python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing。

  multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。

  multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

  需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。

2.Process类的介绍

  • 创建进程的类

Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
  • 参数介绍

group参数未使用,值始终为None
target表示调用对象,即子进程要执行的任务
args表示调用对象的位置参数元组,args=(1,2,‘egon‘,)
kwargs表示调用对象的字典,kwargs={‘name‘:‘egon‘,‘age‘:18}
name为子进程的名称
  • 方法介绍

p.start():启动进程,并调用该子进程中的p.run()
p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
p.is_alive():如果p仍然运行,返回True
p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
  • 属性介绍

p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
p.name:进程的名称
p.pid:进程的pid
p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)

3.Process类的使用

part1:创建并开启子进程的方式

注意:在windows中Process()必须放到# if __name__ == ‘__main__‘:下

由于Windows没有fork,多处理模块启动一个新的Python进程并导入调用模块。
如果在导入时调用Process(),那么这将启动无限继承的新进程(或直到机器耗尽资源)。
这是隐藏对Process()内部调用的原,使用if __name__ == “__main __”,这个if语句中的语句将不会在导入时被调用。

  • 开进程第一种方法

# -*- coding:utf-8 -*-
# /user/bin/python
import time
import random
from multiprocessing import Process
def eat(name):
    print("%s eating" % name)
    time.sleep(random.randint(1,3))
    print("%s eat end" % name)
if __name__ == ‘__main__‘:

    p1 = Process(target=eat, args=("alex",))
    p2 = Process(target=eat, args=("nickle",))
    p3 = Process(target=eat, args=("json",))
    p4 = Process(target=eat, args=("tom",))

    p1.start()
    p2.start()
    p3.start()
    p4.start()
    print("主线程")

"""
输出结果:
主线程
alex eating
nickle eating
json eating
tom eating
nickle eat end
json eat end
alex eat end
tom eat end
"""
  • 开启进程第二种方法

# -*- coding:utf-8 -*-
# /user/bin/python
import time
import random
from multiprocessing import Process
class eat(Process):

    def __init__(self,name):
        super().__init__()
        self.name = name

    def run(self):
        print("%s eating" % self.name)
        time.sleep(random.randint(1,3))
        print("%s eat end" % self.name)
if __name__ == ‘__main__‘:
    p1 = eat("alex")
    p2 = eat("nickle")
    p3 = eat("json")
    p4 = eat("tom")

    p1.start()
    p2.start()
    p3.start()
    p4.start()
    print("主进程")

"""
输出结果:
主进程
alex eating
json eating
nickle eating
tom eating
nickle eat end
tom eat end
json eat end
alex eat end
"""

part2:Process对象的其他方法或属性

  • 进程对象的方法一:terminate,is_alive

# -*- coding:utf-8 -*-
# /user/bin/python
from multiprocessing import Process
import time
import random
class eat(Process):

    def __init__(self,name):
        super().__init__()
        self.name = name

    def run(self):
        print("%s eating" % self.name)
        time.sleep(random.randint(1,3))
        print("%s eat end" % self.name)
if __name__ == ‘__main__‘:
    p = eat("nickle")
    p.start()

    p.terminate() # 关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活
    print(p.is_alive()) # 结果为True
    time.sleep(random.randint(1,3))
    print("go")
    print(p.is_alive())  # 结果为False

"""
输出结果:
True
go
False
"""

注意了:p.join(),是父进程在等p的结束,是父进程阻塞在原地,而p仍然在后台运行

  • 进程对象的方法二:p.daemon=True,p.join

# -*- coding:utf-8 -*-
# /user/bin/python
from multiprocessing import Process
import time
import random
class eat(Process):

    def __init__(self,name):
        super().__init__()
        self.name = name

    def run(self):
        print("%s eating" % self.name)
        time.sleep(random.randint(1,3))
        print("%s eat end" % self.name)
if __name__ == ‘__main__‘:
    p = eat("nickle")
    p.daemon = True # 一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且主进程死,p跟着一起死
    p.start()

    p.join(0.0001) # 等待p停止,等0.0001秒就不再等了
    print("go")

"""
输出结果:
go
"""

# -*- coding:utf-8 -*-
# /user/bin/python
from multiprocessing import Process
import time
import random

def eat(name):
    print("%s eating" % name)
    time.sleep(random.randint(1,3))
    print("%s eat end" % name)
if __name__ == ‘__main__‘:

    p1 = Process(target=eat, args=("alex",))
    p2 = Process(target=eat, args=("nickle",))
    p3 = Process(target=eat, args=("json",))
    p4 = Process(target=eat, args=("tom",))

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

    # 既然join是等待进程结束,那么我像下面这样写,进程不就又变成串行的了吗?
    # 当然不是了,必须明确:p.join()是让谁等?
    # 很明显p.join()是让主线程等待p的结束,卡住的是主线程而绝非进程p,

    # 详细解析如下:
    # 进程只要start就会在开始运行了,所以p1-p4.start()时,系统中已经有四个并发的进程了
    # 而我们p1.join()是在等p1结束,没错p1只要不结束主线程就会一直卡在原地,这也是问题的关键
    # join是让主线程等,而p1-p4仍然是并发执行的,p1.join的时候,其余p2,p3,p4仍然在运行,等#p1.join结束,可能p2,p3,p4早已经结束了,这样p2.join,p3.join.p4.join直接通过检测,无需等待
    # 所以4个join花费的总时间仍然是耗费时间最长的那个进程运行的时间

    p1.join()
    p2.join()
    p3.join()
    p4.join()
    print("主线程")
# 简化方式:
#     p_lis = [p1, p2, p3, p4]
#     for p in p_lis:
#         p.start()
#
#     for p in p_lis:
#         p.join()
"""
输出结果:
alex eating
nickle eating
json eating
tom eating
nickle eat end
tom eat end
alex eat end
json eat end
主线程
"""

  • 进程对象的其他方法:name,id

# -*- coding:utf-8 -*-
# /user/bin/python
from multiprocessing import Process
import time
import random

class eat(Process):

    def __init__(self,name):
        # self.name=name
        # super().__init__() #Process的__init__方法会执行self.name=Piao-1,
        #                    #所以加到这里,会覆盖我们的self.name=name

        #为我们开启的进程设置名字的做法
        super().__init__()
        self.name = name

    def run(self):
        print("%s eating" % self.name)
        time.sleep(random.randint(1,3))
        print("%s eat end" % self.name)
if __name__ == ‘__main__‘:

    p = eat("nickle")
    p.start()
    print("主线程")
    print(p.pid)
    print(p.name)

"""
输出结果:
主线程
9100
nickle
nickle eating
nickle eat end
"""

4.进程间通信(IPC)方式一:队列

进程彼此之间互相隔离,要实现进程间通信,即IPC,multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的

  创建队列的类(底层就是以管道和锁定的方式实现):

1 Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。

  参数介绍:

主要方法:

1 q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
2 q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
3
4 q.get_nowait():同q.get(False)
5 q.put_nowait():同q.put(False)
6
7 q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
8 q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
9 q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样

其他方法:

1 1 q.cancel_join_thread():不会在进程退出时自动连接后台线程。可以防止join_thread()方法阻塞
2 2 q.close():关闭队列,防止队列中加入更多数据。调用此方法,后台线程将继续写入那些已经入队列但尚未写入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将调用此方法。关闭队列不会在队列使用者中产生任何类型的数据结束信号或异常。例如,如果某个使用者正在被阻塞在get()操作上,关闭生产者中的队列不会导致get()方法返回错误。
3 3 q.join_thread():连接队列的后台线程。此方法用于在调用q.close()方法之后,等待所有队列项被消耗。默认情况下,此方法由不是q的原始创建者的所有进程调用。调用q.cancel_join_thread方法可以禁止这种行为

应用:

时间: 2024-12-18 15:51:05

Python之进程与线程的相关文章

【Python】进程和线程

多任务的实现方式有三种方式: 1.多进程 2.多线程 3.多进程+多线程(这种比较复杂,实际很少采用) [多进程] 1.在mac中创建子进程使用Python封装的fork()系统调用. import os pid = os.fork() pid 2.在windows上的实现. 使用multiprocessing模块的Process类. 为了观察,还加了一下端代码~ # -*- coding: utf-8 -*- from multiprocessing import Process import

Python下进程与线程的原理及区别

对于所有的语言都可能会遇到进程和线程的问题,一般情况下线程是由进程产生的,一个进程产生多个线程来按照一定的规则(Python下根据CPU调度算法和全局进程锁)来利用CPU,我们称之为多线程模式:而一个进程在产生的同时,同时会生成一个主线程,如果程序生成多个进程,那么每个进程都会产生一个线程,多个程序按照一定的规则去利用CPU,我们称之为多进程模式.                 Python下多线程与多进程关系图原理如下所示:

Python(十) 进程、线程、协程篇

本节内容 1.操作系统发展史    2.进程.与线程区别    3.Python GIL全局解释器锁    4.线程        1.语法        2.join        3.线程锁 Lock.RLock.信号量        4.将线程变为守护进程        5.Event事件        6.queue队列        7.生产者消费者模型        8.Queue队列        9.开发一个线程池    5.进程        1.语法        2.进程间

python的进程与线程

一.进程与线程的相关概念 1.什么是进程 进程是一个程序在一个数据集上的一次动态执行过程. 进程一般由程序,数据集,进程控制块三部分组成. 2.什么是线程 线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID.程序计数器.寄存器集合和堆栈共同组成.线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能.线程没有自己的系统资源. 3.线程与进程的关系 线程不能独立存在,一个线程肯定有一个父进程.进程也不能独立存在,它一定也包含一个进程.可以理解为进程是

python之进程、线程、协程的调度原理

进程.线程和协程的调度和运行原理总结. 进程.线程的调度策略介绍 linux的操作系统详细调度策略可参考:http://blog.csdn.net/gatieme/article/details/51872659 linux中的进程主要有三种调度策略: 优先级调度:将进程分为普通进程和实时进程: 先进先出(队列)调度:实时进程先创建的先执行,直到 轮转调度(时间片):达到一定的CPU执行时间后强制切换: 多进程程序的调度其实还是线程的调度,线程才是CPU调度的基本单位:在同一个进程内线程切换不会

《Python》进程收尾线程初识

from multiprocessing import Manager 把所有实现了数据共享的比较便捷的类都重新又封装了一遍,并且在原有的multiprocessing基础上增加了新的机制list.dict 机制:支持的数据类型非常有限 list.dict都不是数据安全的,需要自己加锁来保证数据安全 from multiprocessing import Manager,Process,Lock def work(d,lock): with lock: d['count'] -= 1 if __

python学习进程和线程中碰到的问题以及问题解惑

python中的os.getpid() os.getpid()是获取的是当前进程的进程号,os.getppid()是获取当前进程的父进程的进程号 python 中__name__ = '__main__' 的作用 eg: def main(): print "we are in %s"%__name__ if __name__ == '__main__': main() 这个函数定义了一个main函数,我们执行一下该py文件发现结果是打印出"we are in __main_

Python之进程、线程、协程篇

本章内容: 线程(线程锁.threading.Event.queue 队列.生产者消费者模型.自定义线程池) 进程(数据共享.进程池) 协程 线程 Threading用于提供线程相关的操作.线程是应用程序中工作的最小单元,它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. threading 模块建立在 _thread 模块之上.thread 模块以低级.原始的方式来处理和控制线程,而 threading

【Python】[进程和线程]多进程,多线程,ThreadLocal,进程VS.线程,分布式进程

1.多进程,multiprocessing模块,   进程间的通信:Queue[队列],Pipes[管子]2.多线程,    注意:线程公用变量,混乱   解决方法Lock:因为只有一个锁,所以当要执行统一个函数的时候,只有在解锁的前提下才能 执行. balance = 0 lock = threading.Lock() def run_thread(n): for i in range(100000): # 先要获取锁: lock.acquire() try: # 放心地改吧: change_