多任务编程进程线程

Posted xiongsheng666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多任务编程进程线程相关的知识,希望对你有一定的参考价值。

多任务编程进程线程

01.多任务

1.1多任务的认识

多任务是指在同一个时间内执行多个任务。

例如 :现在电脑安装的操作系统都是多任务操作系统,可以同时运行着多个软件。

1.2多任务的两种表现形式

并发 和 并行

并发 在一段时间内交替执行多个任务

​ 并发的任务数量大于cpu的核心数

并行

在一段时间内真正同时一起执行多个任务

并行:任务数量小于或等于cpu的核心数

  1. 使用多任务能充分利用cpu资源,提高程序的执行效率,让你的程序具备处理多个任务的能力

  2. 多任务的执行有两种方式:

    并发: 在一段时间内交替去执行多个任务
    并行:在一段时间内真正的同时一起执行多个任务

进程

02. 进程的介绍

2.1程序中实现多任务的方式

在python中,想要实现多任务可以使用多进程来完成

2.2 进程

进程(process)是资源分配的最小单位,它是操作系统进行资源分配调度运行的基本单位
通俗理解: 一个正在运行的程序就是一个进程,例如正在运行的qq, 微信等,他们都是一个进程


注意:一个程序运行至少有一个进程

2.3 多进程的作用

提高程序的执行效率

2.4 技术小结

  1. 进程(process)是资源分配的最小单位
  2. 多进程是python程序中实现多任务的一种方式,使用多进程可以大大提高程序的执行效率

03. 多进程完成多任务

3.1 进程的创建步骤

1. 导入进程包
	import multiprocessing
2. 通过进程类创建进程对象
	进程对象 = multiprocessing.Process()
3.启动进程执行任务
	进程对象.start()

3.2 进程创建与启动

# 创建子进程
coding_process = multiprocessing.Process(target=coding)
# 创建子进程
music_process = multiprocessing.Process(target=music)
# 启动进程
coding_process.start()
music_process.start()

看代码效果暂时如下:

参数名说明
target 进程执行的函数名
args 以元组的方式给执行任务传参
kwargs 以字典方式给执行任务传参

Process 源码

进程对象 = multiprocessing.Process(target = 任务名 这里是函数名 )
参数说明

target 执行的目标任务名,这里指的是函数名 (方法名)
name 进程名,一般不用设置
group 进程组,目前只能使用None

注意: 进程执行带有参数的任务传参有两种方式
1.元组方式传参 : 元组方式传参一定要和参数的传参顺序保持一致
2.字典方式传参: 字典方式传参字典中的key一定要和参数名保持一致

04. 获取进程编号

4. 1 进程编号的作用:

当程序中进程的数量越来越多时,如果没有办法区分主进行和子进程还有不同的子进程,那么就无法进行有效的进程管理。
为了方便管理实际上每个进程都是有自己编号的

4. 2 获取进程编号的两种方式:

1.获取当前的进程编号
2.获取当前父进程编号

os.getpid()的使用


getpid源码

或者

查看当前进程

os.getppid()的使用 获取父进程id

getppid源码

4. 2 根据进程编号杀死指定进程

  1. 获取当前进程编号
os.getpid() 或者multiprocessing.current_process().pid

2.获取当前父进程编号

os.getppid()

3.根据进程编号杀死指定进程

os.kill()

05. 进程的注意点

  1. 进程之间不共用全局变量
  2. 主进程会等待所有的子进程执行结束再结束

线程

06. 线程的介绍

  • 在python中想要实现多任务除了使用进程,还可以使用线程来完成。线程是实现多任务的另一种方式

6.1 线程的概念

线程是进程中执行代码的一个分支,每个执行分支 每一个线程想要工作执行代码需要cpu进行调度
线程是cpu调度的基本单位,每个进程至少都有一个线程,而这个线程就是我们通常说的主线程

6.2 线程的作用

  • 多线程可以完成多任务

多线程效果图:

说明: 程序启动默认会有一个主线程,程序员自己创建的线程可以称之为子线程,多线程可以完成多任务

07. 多线程完成任务(多线程的使用)

7.1 导入线程模块

#导入线程模块
import threading

7.2 线程类Thread参数说明

看代码:

Thread源码

group: 线程组,目前只能使用None
target: 执行的目标任务名
args: 以元组的方式给执行任务传参
kwargs: 以字典方式给执行任务传参
name: 线程名,一般不用设置

08. 多线程执行带有参数的任务

Thread类执行任务并给任务传参数有两种方
args 表示以元组的方式给执行任务传参
kwargs 表示以字典方式给执行任务传参

8.1 args参数的使用

8.2 kwargs参数的使用

线程执行任务并传参有两种方式:

  • 元组方式传参(args) :元组方式传参一定要和参数的顺序保持一致。
  • 字典方式传参(kwargs):字典方式传参字典中的key一定要和参数名保持一致。

09. 主线程和子线程的结束顺序

主线程会等待所有的子线程执行结束再结束

9.1 设置守护主线程

要想主线程不等待子线程执行完成可以设置守护主线程。

9.1 小结

设置守护主线程的目的是主线程退出子线程销毁,不让主线程再等待子线程去执行。

设置守护主线程有两种方式:

  1. threading.Thread(target=work, daemon=True)
  2. 线程对象.setDaemon(True)

10.线程间的执行顺序

10.1 线程间的执行是无序的

for i in range(5):
	sub_thread = threading.Thread(target=task)
	sub_thread.start()

10.2 获取当前的线程信息

# 通过current_thread方法获取线程对象
current_thread = threading.current_thread()
# 通过current_thread对象可以知道线程的相关信息,例如被创建的顺序
print(current_thread)

线程之间执行是无序的,是由CPU调度决定某个线程先执行的

11. 线程间共享全局变量

多个线程都是在同一个进程中 , 多个线程使用的资源都是同一个进程中的资源 , 因此多线程间是共享全局变量

12. 互斥锁

12. 1互斥锁的认识

互斥锁: 对共享数据进行锁定,保证同一时刻只能有一个线程去操作。

注意:
互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程需要等待,等互斥锁使用完释放后,其它等待的线程再去抢这个锁。

12. 2互斥锁的使用

hreading模块中定义了Lock变量,这个变量本质上是一个函数,通过调用这个函数可以获取一把互斥锁。
互斥锁使用步骤:

# 创建锁
mutex = threading.Lock()

# 上锁
mutex.acquire()

...这里编写代码能保证同一时刻只能有一个线程去操作, 对共享数据进行锁定...

# 释放锁
mutex.release()

注意点:

acquire和release方法之间的代码同一时刻只能有一个线程去操作
如果在调用acquire方法的时候 其他线程已经使用了这个互斥锁,那么此时acquire方法会堵塞,直到这个互斥锁释放后才能再次上锁。

互斥锁能够保证多个线程访问共享数据不会出现数据错误问题

12. 3 互斥锁小结

互斥锁的作用就是保证同一时刻只能有一个线程去操作共享数据,保证共享数据不会出现错误问题
使用互斥锁的好处确保某段关键代码只能由一个线程从头到尾完整地去执行
使用互斥锁会影响代码的执行效率,多任务改成了单任务执行
互斥锁如果没有使用好容易出现死锁的情况

13. 死锁

死锁: 一直等待对方释放锁的情景就是死锁
死锁的结果:会造成应用程序的停止响应,不能再处理其它任务了。

13. 1 死锁实例

需求:
根据下标在列表中取值, 保证同一时刻只能有一个线程去取值

13. 1 避免死锁

在合适的地方释放锁

14. 进程和线程对比

14.1 进程和线程的对比 三个方向

1.关系对比
2.区别对比
3.优缺点对比

14.2 关系对比

1.线程是依附在进程里面的,没有进程就没有线程
2.一个进程默认提供一条线程,进程可以创建多个线程
进程是计算机资源分配的最小单位,线程是cpu调度的基本单位
一个进程中可以有多个线程,同一个进程中的线程可以共享进程中的资源

14.3 区别对比

1.进程之间不共享全局变量
2.线程之间共享全局变量,但是要主要资源竞争问题。解决办法:互斥锁或者线程同步
3.创建进程的资源开销 要比创建线程的资源开销要大
4.进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
5.线程不能够独立执行,必须依存在进程中
6.多进程 开发比单进程多线程开发的稳定性要强

14.4 优缺点对比

进程的优点: 可以用多核
进程的缺点: 资源开销大

线程的优点:资源开销小
线程的缺点: 不能使用多核
	

小结:

  • 进程和线程都是完成多任务的一种方式
  • 多进程要比多线程消耗的资源多,但是多进程开发比单进程多线程开发稳定性要强,某个进程挂掉了不会影响其他进程。
  • 多进程可以使用cpu的多核运行,多线程可以共享全局变量
  • 线程不能单独执行必须依附在进程里面

15. 强制杀死结束线程

工作中遇到线程阻塞,导致程序无法结束,需要强制杀死结束线程
参考案例

http://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread-in-python


代码展示如下

  1. 用到的模块
import threading
import time
import inspect
import ctypes


def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")


def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)

class TestThread(threading.Thread):
    def run(self):
        print("线程开始")
        while True:
            time.sleep(0.1)
            print("线程 进行中")


if __name__ == "__main__":
    t = TestThread()
    t.start()  # 开启启动

    time.sleep(2)  # 等待线程进行2秒时间
    stop_thread(t)  # 结束线程 调用函数即可
    print("线程 结束")

(本人亲手可测 已用于项目 )

以上是关于多任务编程进程线程的主要内容,如果未能解决你的问题,请参考以下文章

多线程基础知识总结

进程与线程的区别

线程与进程的区别?

Python核心编程总结(五多任务编程之进程与线程)

Python核心编程总结(五多任务编程之进程与线程)

JAVA面试题:多线程