学习笔记(二十二)—— 了解进程和线程

Posted 别呀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习笔记(二十二)—— 了解进程和线程相关的知识,希望对你有一定的参考价值。

一、进程

1.1、什么是程序

程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。而进程则是在处理机上的一次执行过程,它是一个动态的概念。这个不难理解,其实进程是包含程序的,进程的执行离不开程序,进程中的文本区域就是代码区,也就是程序。


1.2、了解进程

进程就是一段程序的执行过程。它是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

简单的来讲进程的概念主要有两点:
进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(textregion)、数据区域(dataregion)和堆栈(stackregion)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。
进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程。


1.3、进程的三种状态

进程有三个状态,就绪、运行和阻塞。

  • 就绪态:其实就是获取了除cpu外的所有资源,只要处理器分配资源就可以马上执行。
  • 运行态:获得了处理器分配的资源,程序开始执行。
  • 阻塞态:当程序条件不够时候,需要等待条件满足时候才能执行,如等待i/o操作时候,此刻的状态就叫阻塞态。

1.4、多进程

Python中的多进程是通过multiprocessing包来实现的,和多线程的threading.Thread差不多,它可以利用multiprocessing.Process对象来创建一个进程对象。这个进程对象的方法和线程对象的方法差不多也有start(),run(), join()等方法,其中有一个方法不同Thread线程对象中的守护线程方法是setDeamon,而Process进程对象的守护进程是通过设置daemon属性来完成的。

Python多进程的实现方法,和多线程类似
实现示例:

from multiprocessing import  Process
def fun1(name):
    print('%s进程开启了' %name)

if __name__ == '__main__':
    for i in range(5):  #开启5个子进程执行fun1函数
        p = Process(target=fun1,args=(i,)) #实例化进程对象
        p.start()
        p.join()         #主进程阻塞

结果:

0进程开启了
1进程开启了
2进程开启了
3进程开启了
4进程开启了

join方法:是让主进程等待子进程运行完毕后再执行主进程的。(即主进程阻塞)
类的方式创建线程

from multiprocessing import  Process
class MyProcess(Process): #继承Process类
    def __init__(self,name):
        super(MyProcess,self).__init__()
        self.name = name

    def run(self):
        print(f'{self.name}进程开启了')

if __name__ == '__main__':
    for i in range(5):  #开启5个子进程执行fun1函数
        p = MyProcess(str(i)) #实例化进程对象
        p.start()
        p.join()

Process类的其他方法

构造方法:
Process([group [, target [, name [, args [, kwargs]]]]])
  group: 线程组 
  target: 要执行的方法
  name: 进程名
  args/kwargs: 要传入方法的参数

实例方法:
  is_alive():返回进程是否在运行,bool类型。
  join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。
  start():进程准备就绪,等待CPU调度
  run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。
  terminate():不管任务是否完成,立即停止工作进程

属性:
  daemon:和线程的setDeamon功能一样
  name:进程名字
  pid:进程号

二、线程

2.1、了解线程

通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源,我们把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位


2.2、了解多线程

多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。

多线程就像火车的每一节车厢,而进程则是火车。车厢离开火车是无法跑动的,同理火车也不可能只有一节车厢。多线程的出现就是为了提高效率。


2.3、进程与线程的区别

进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

  1. 简而言之,一个程序至少有一个进程,一个进程至少有一个线程。
  2. 线程的划分尺度小于进程,使得多线程程序的并发性高。
  3. 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
  4. 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
  5. 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区。

2.4、多线程实现

Python的标准库提供了两个模块:_threadthreading,_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行。

补充知识点:主线程和子线程
程序执行时,程序本身就是一个线程,叫主线程。
手动创建的线程,叫子线程。
主线程的执行中不会等待子线程执行完毕,就会直接执行后面的代码创建线程。

实现示例

from threading import Thread
import time

def run(name):
    print(name,'线程执行了!')
    time.sleep(4)
#创建线程
t1 = Thread(target=run,args=('t1',))
t2 = Thread(target=run,args=('t2',))
#启动线程
t1.start()  
t2.start()

----结果:
t1 线程执行了!
t2 线程执行了!

类的方式创建线程

from threading import Thread
import time

class MyThread(Thread):
    def __init__(self,name):
        super(MyThread,self).__init__()
        self.name = name

    def run(self):
        print(self.name, '线程执行了!')
        time.sleep(4)

t1 = MyThread('t1')
t2 = MyThread('t2')
t1.start()
t2.start()

其他方法:

isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。
threading模块提供的一些方法:
threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount():返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

2.5、线程锁

多线程可以同时运行多个任务但是当多个线程同时访问共享数据时,可能导致数据不同步,甚至错误!因此,就要使用线程锁。

线程锁:当有多个线程访问同一对象的加锁方法/代码块时,同一时间只有一个线程在执行,其余线程必须要等待当前线程执行完之后才能执行该代码段。但是,其余线程是可以访问该对象中的非加锁代码块的。

用法

#创建锁
lock = threading.Lock()
#锁定
lock.acquire([timeout])
#释放
lock.release()

优缺点

优点:保证资源同步
缺点:
等待肯定会慢

示例

import time
import threading

lock = threading.Lock()
num = 100
#出售
def sail(name):
    lock.acquire()   #上锁
    global num
    if num>0:
        num-=1
    print(name,'出售了一张电影票,还剩:',num,'张')
    lock.release()   #释放锁

#售票
while True:
    if num>0:
        A = threading.Thread(target=sail, args=('A售票员',))
        B = threading.Thread(target=sail, args=('B售票员',))
        A.start()
        B.start()
    else:
        break

2.6、全局解释锁(GIL)

不管系统CPU核心是多少,都保证python程序中同一时间点只能执行一个线程。它本本意上是为了实现数据安全问题,但从结果上却造成了cpu资源上的浪费,影响了程序执行的效率,因此弊大于利。(重点记)

我们可以通过使用多进程来解决GIL所造成的问题

以上是关于学习笔记(二十二)—— 了解进程和线程的主要内容,如果未能解决你的问题,请参考以下文章

Python学习笔记(二十七)多进程 (进程和线程开始)

Java基础学习笔记二十一 多线程

Objective-C学习笔记(二十二)——初始化方法init的重写与自己定义

Python学习笔记(二十八)多线程

Java基础二十二

爬虫学习笔记(二十二)—— Mitmproxy