首页 > 系统相关 >实现多任务之进程与线程

实现多任务之进程与线程

时间:2023-10-17 19:26:30浏览次数:35  
标签:__ work print 线程 time 进程 多任务

进程与线程

一、多任务概念

1、举个栗子

image-20231013082703303

比如在网盘下载资料的时候,为什么要多个资料同时下载?

答:多个任务同时下载可以大大提高程序执行的效率。

多任务的最大好处就是充分利用好CPU资源,提高程序的执行效率。

2、什么是多任务

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

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

3、多任务的两种表现形式

① 并发

② 并行

4、并发操作

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

例如:对于单核cpu处理多任务,操作系统轮流让各个任务交替执行。例如:软件1执行了0.01秒,切换到软件2,软件2执行了0.01秒,再切换到软件3,执行了0.01秒....这样反复执行下去。

实际上,每个软件都是交替执行的,但是由于cpu的执行速度实在太快了,表面上我们感觉这些软件都在同时执行一样,这里需要注意单核cpu是并发执行多任务的

image-20231013083756624

5、并行操作

并行:在一段时间内真正的同时执行多个任务。

对于多核cpu处理多任务,操作系统会给cpu的每个内核安排一个执行的任务,多个内核是真正一起执行多个任务。需要注意多个cpu是并行的执行多任务,始终有多个任务一起执行。

image-20231013084108879

二、进程

1、进程的概念

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

进程(process)是资源分配的最小单位,它是操作系统进行资源分配和调度运行的基本单位,通俗理解:一个正在运行的程序就是一个进程。

例如:正在运行的qq或微信都是一个进程。

注:一个正在运行的程序至少有一个进程

2、多进程的作用

未使用多进程:

image-20231013092205176

思考:

​ 图中是一个非常简单的程序 , 一旦运行hello.py这个程序 , 按照代码的执行顺序 , func_a函数执行完毕后才能执行func_b函数 . 如果可以让func_a和func_b同时运行 , 显然执行hello.py这个程序的效率会大大提升 .

image-20231013092233751

使用了多进程:

image-20231013092256442

三、多进程完成多任务

1、基本语法

# 导入进程包
import multiprocessing

# 通过进程类创建进程对象
进程对象 = multiprocessing.process()

# 启动进程执行任务
进程对象.start()
进程对象 = multiprocessing.process([group[,target = 任务名[,name]]])

参数说明:

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

2、进程的创建与启动

import multiprocessing
import time


def music():
    for i in range(3):
        print('正在听音乐...')
        time.sleep(0.2)


def coding():
    for i in range(3):
        print('正在写代码...')
        time.sleep(0.2)


if __name__ == '__main__':
    # 创建子进程对象
    music_process = multiprocessing.Process(target=music)
    code_process = multiprocessing.Process(target=coding)

    # 启动进程对象
    music_process.start()
    code_process.start()

image-20231013095729883

3、多进程执行带有参数的任务

process([group[,target [,name [,args [,kwargs ]]]]])

参数说明:

  • args:以元组方式给执行任务传参,args代表调用对象的位置参数元组,args = (1,2,'anen')

  • kwargs: 以字典的方式给执行任务传参,kwargs代表调用对象的字典,kwargs =

1.元组方式传参:元组方式传参一定要和参数的顺序保持一致

2.字典方式传参:字典方式传参中的key一定要和参数名一致

import multiprocessing
import time


# 参数n代表听音乐的次数
def music(n):
    for i in range(n):
        print('正在听音乐...')
        time.sleep(0.2)


# 参数t代表休眠时间
def coding(t):
    for i in range(3):
        print('正在写代码...')
        time.sleep(t)


if __name__ == '__main__':
    # 创建子进程对象
    music_process = multiprocessing.Process(target=music, args=(3,))  # 以元组方式传参
    code_process = multiprocessing.Process(target=coding, kwargs={'t': 0.2})  # 以字典方式传参

    # 启动子进程
    music_process.start()
    code_process.start()

四、获取进程编号

1、进程编号的作用

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

2、获取进程编号

获取当前进程的编号

os.getpid()

**获取当前父进程的编号 ** ppid = parent pid

os.getppid()

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

os.kill(进程编号pid, 传递的信号)

常见的传递信号:

  • 9:强制杀掉PID进程
  • 15:通知PID进程,正常结束
import os
import multiprocessing


def working():
    print(f'当前子进程的编号为{os.getpid()}')
    print(f'当前子进程的父进程的编号为{os.getppid()}')
    for i in range(3):
        print('working...')


if __name__ == '__main__':
    # 获取当前进程的编号
    print(f'当前进程的编号:{os.getpid()}')1

    # 创建子进程并或子进程和其父进程的编号
    working_process = multiprocessing.Process(target=working)
    working_process.start()

image-20231013163825378

  • 主进程默认等待所有子进程执行结束再结束

五、进程的注意点

1、进程之间不共享全局变量

实际上创建一个子进程就是把主进程的资源进行拷贝产生了一个新的进程,这里的主进程和子进程是相互独立的

image-20231013172942691

import time
import multiprocessing

# 定义一个全局变量
my_list = []


def write_data():
    for i in range(3):
        my_list.append(i)
        print('add:', i)
    print(my_list)


def read_data():
    print(my_list)


if __name__ == '__main__':
    # 创建一个写数据的子进程
    write_process = multiprocessing.Process(target=write_data)
    # 创建一个读数据的子进程
    read_process = multiprocessing.Process(target=read_data)

    # 启动子进程
    write_process.start()
    time.sleep(1)  # 休眠1秒
    read_process.start()

    for i in range(3, 6):
        my_list.append(i)
    print(my_list)

image-20231013172706404

  • 主进程与子进程的全局变量也是不共享的

原理分析:

image-20231013173056474

三个进程分别操作的都是自己进程里面的全局变量my_list,不会对其他变量里面的全局变量产生影响,所以进程之间不共享全局变量,只不过进程之间的全局变量的名称相同而已,但是操作的不是一个进程里面的全局变量

知识小结

创建子进程会对主进程的资源进行拷贝,也就是说子进程是主进程的一个副本,好比是一对双胞胎,之所以进程之间不共享全局变量,是因为操作的不是同一个进程里面的全局变量,只不过不同进程里面的全局变量名字相同而已。

2、主进程与子进程的结束顺序

主进程会默认等待所有子进程执行结束再结束

import multiprocessing
import time


def work():
    for i in range(5):
        print('working...')
        time.sleep(0.2)


if __name__ == '__main__':
    work_process = multiprocessing.Process(target=work)
    # 子进程执行1秒
    work_process.start()

    # 延迟0.5秒
    time.sleep(0.5)
    print(f'主进程执行结束')

image-20231013212804826

  • 按道理说,主进程执行结束之后,子进程也要结束了

image-20231013212837048

解决方案一:设置守护线程

设置守护主线程,主进程结束之后子进程直接销毁,不再执行子进程中的代码

子进程名称.daemon = True
  • 在启动子进程之前,设置守护线程
import multiprocessing
import time


def work():
    for i in range(5):
        print('working...')
        time.sleep(0.2)


if __name__ == '__main__':
    work_process = multiprocessing.Process(target=work)
    # 设置守护主进程,在主进程退出后子进程直接销毁,不再执行子进程中的代码
    work_process.daemon = True
    work_process.start()

    # 让主进程等待0.5秒
    time.sleep(0.5)
    print('主进程执行结束!')

image-20231013213635394

解决方案二:销毁子进程

子进程名称.terminate()
  • 在主程序退出之前销毁所有的子进程
import multiprocessing
import time


def work():
    for i in range(5):
        print('working...')
        time.sleep(0.2)


if __name__ == '__main__':
    work_process = multiprocessing.Process(target=work)
    work_process.start()

    # 让主进程等待0.5秒
    time.sleep(0.5)
    # 让子进程直接销毁,表示终止执行,主程序退出之前,把所有子进程销毁就可以了
    work_process.terminate()
    print('主进程执行结束!')

image-20231013214303445

六、线程

1、线程的作用

线程是多任务执行的另外一种方式

进程是资源分配的最小单位,一旦创建一个线程就会分配一定的资源,就像在QQ与两个人同时聊天需要打开两个QQ软件比较浪费资源。

线程是程序执行的最小单位,实际上进程只负责分配资源,真正利用资源执行程序的是线程,也就是说一个进程是线程的容器,一个进程里至少拥有一个线程来负责执行程序,同时线程不拥有系统资源,只需要一点在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。这就像通过一个QQ软件(一个进程)打开两个窗口(两个线程)与两个好友同时聊天一样,实现多任务的同时也节省了资源。

那么什么时候使用多线程,什么时候使用多进程呢?

  • 偏cpu计算任务 => 推荐使用多线程 => 推荐使用多进程 (需要使用到较多的系统资源)
  • 偏IO型任务 => 文件操作/网络连接 => 推荐使用多线程(系统资源需要少,且需要频繁操作)

注意:多进程在cpu允许的情况下是并行执行的,而同属一个进程内的多线程多任务只能使用一个cpu,是并发执行的

多线程的实现:

image-20231017105520474

  • 线程包含在进程的内部,一个进程理论上至少含有一个线程
  • 进程是资源分配的最小单位(申请资源),线程是程序执行的最小单位(专门用于执行程序的)
  • 同属一个进程的多个线程共享进程内的所有资源

2、多线程完成多任务

基本语法:

# 导入线程模块
import threading
# 通过线程类创建线程对象
线程对象 = threading.Thread(target = 任务名)
# 启动线程执行任务
线程对象.start()
线程对象 = threading.Thread([group[,target [,name [,args [,kwargs ]]]]])

参数说明:

  • target :执行的目标任务名,这里指函数名(方法名)
  • name :线程名,一般不用设置
  • group :线程组,目前只能使用None
  • args:在创建线程的时候通过元组的方式传入参数
  • kwargs:在创建线程的时候通过字典的方式传入参数
import threading
import time


def music(n):
    for i in range(n):
        print('正在听音乐...')
        time.sleep(0.1)


def coding(t):
    for i in range(3):
        print('正在写代码....')
        time.sleep(t)


if __name__ == '__main__':
    # 创建多线程,并以元组或字典的方式传入参数
    music_thread = threading.Thread(target=music, args=(3,))
    code_thread = threading.Thread(target=coding, kwargs={'t': 0.2})

    # 启动线程
    music_thread.start()
    code_thread.start()

image-20231017154329248

3、线程之间共享全局变量

多进程实现多任务:无法共享全局变量

多线程实现多任务:因为所有线程都位于一个进程中,不仅资源是共享的,全局变量也是共享的

import threading
import time

my_list = []


def write_data():
    for i in range(3):
        my_list.append(i)
    print(my_list)


def read_data():
    print(my_list)


if __name__ == '__main__':

    write_thread = threading.Thread(target=write_data)
    read_thread = threading.Thread(target=read_data)

    write_thread.start()
    time.sleep(1)
    read_thread.start()

    for i in range(3, 6):
        my_list.append(i)
    print(my_list)

image-20231017161254277

4、主线程与子线程的结束顺序

对比进程

主线程也是会默认等待所有的子进程执行结束后主线程再结束

import threading
import time


def work():
    for i in range(3):
        print('working...')
        time.sleep(0.5)


if __name__ == '__main__':
    work_thread = threading.Thread(target=work)
    work_thread.start()

    time.sleep(1)
    print('主线程执行结束')

image-20231017163738075

设置守护线程的两种方式

方式一:

# 创建子线程的同时设置守护线程
work_thread = threading.Thread(target=work,daemon = True)

方式二:

# 通过设置方法设置守护线程
work_thread = threading.Thread(target=work)
work_thread.setDaemon(True)

案例:

import threading
import time


def work():
    for i in range(3):
        print('working...')
        time.sleep(0.5)


if __name__ == '__main__':
    # 创建的线程的同时设置守护主线程
    # work_thread = threading.Thread(target=work, daemon=True)
    work_thread = threading.Thread(target=work)
    work_thread.setDaemon(True)
    work_thread.start()

    time.sleep(1)
    print('主线程执行结束')

image-20231017164819593

5、线程的执行顺序

思考:当我们在进程中创建了多个线程,其线程之间是如何执行的呢,按顺序执行?一起执行?还是其他的执行方式呢?

image-20231017170934475

答:线程之间的执行顺序是无序的(由cpu的调度算法决定的)

验证:获取当前线程的线程(current_thread)

# 通过current_thread()方法获取当前线程对象
current_thread = threading.current_thread()

# 通过threading.current_thread对象可以知道线程的相关信息,比如创建的顺序
print(current_thread)

线程的执行顺序:

import threading
import time


def get_info():
    # 防止执行速度过快,在另外一个线程启动的时候,该线程已经执行结束
    time.sleep(0.3)
    current_thread = threading.currentThread()
    print(current_thread)


if __name__ == '__main__':
    # 按顺序创建线程
    for i in range(5):
        sub_thread = threading.Thread(target=get_info)
        sub_thread.start()

image-20231017173351456

  • 线程之间的执行是无序的,是由cpu调度决定哪个线程先执行的。

6、案例之多线程实现TCP服务器端开发

import socket
import threading


class WebServer(object):
    def __init__(self):
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.bind(('', 8080))
        self.tcp_server_socket.listen(128)

    def handle_message(self, new_socket, ip_port):
        content = new_socket.recv(1024).decode('gbk')
        print(f'接收到客户端{ip_port}发送的数据:{content}')
        # reply = input('请输入服务器回复的信息内容:')
        new_socket.send('你好!'.encode('gbk'))

        new_socket.close()

    def start(self):
        while True:
            new_socket, ip_port = self.tcp_server_socket.accept()
            # 每接收到一个客户端的请求连接就创建一个线程
            handle_thread = threading.Thread(target=self.handle_message, args=(new_socket, ip_port))
            handle_thread.start()


if __name__ == '__main__':
    ws = WebServer()
    ws.start()

七、进程与线程对比

关系对比

  1. 线程是依附在进程里面的,没有进程就没有线程
  2. 一个进程默认提供一条线程,进程可以创建多个线程

区别对比

  1. 进程之间是不共享全局变量的,线程是共享全局变量的
  2. 创建进程的资源开销要比创建线程的资源开销要大
  3. 进程是操作系统资源分配的基本单位,线程是cpu调度的基本单位
  4. 线程不能独立执行,必须依存在进程中

优缺点对比

1、进程优缺点:

​ 优点:适合做cpu密集型应用,因为可以使用多核

​ 缺点:资源开销大

2、线程优缺点:

​ 优点:适合做IO密集型应用(文件、网络),资源开销小,因为只需要进程中的一点点资源就可以运行

​ 缺点:不能使用多核

点击查看代码

标签:__,work,print,线程,time,进程,多任务
From: https://www.cnblogs.com/luoluoange/p/17770446.html

相关文章

  • JDK21的虚拟线程是什么?和平台线程什么关系?
    虚拟线程(VirtualThread)是JDK而不是OS实现的轻量级线程(LightweightProcess,LWP),由JVM调度。许多虚拟线程共享同一个操作系统线程,虚拟线程的数量可以远大于操作系统线程的数量。在引入虚拟线程之前,java.lang.Thread包已经支持所谓的平台线程,也就是没有虚拟线程之前,我们一直......
  • 在Matplotlib中使用多线程multiprocessing举例
    在Matplotlib中使用多线程Matplotlib提供了一些机制来支持多线程的使用,比如使用matplotlib.pyplot.switch_backend()方法指定可用的图形后端或使用matplotlib.figure.Figure对象的canvas属性来实现绘图。但是,这些机制都需要特别小心地管理和控制,否则会引发线程之间的数据竞争和访......
  • 使用Guava的ListenableFuture完成异步多线程任务并返回结果
    privatestaticExecutorServiceexecutors=newThreadPoolExecutor(5,20,0L,TimeUnit.MILLISECONDS,newLinkedBlockingQueue<Runnable>(10),newThreadFactoryBuilder().setNameFormat("抓数据线程-%d").build());publicstaticvoidmain(String[]arg......
  • Python多进程之分享(multiprocessing包)
    threading和multiprocessing(可以阅读Python多线程与同步)multiprocessing包是Python中的多进程管理包。与threading.Thread类似,它可以利用multiprocessing.Process对象来创建一个进程。该进程可以运行在Python程序内部编写的函数。该Process对象与Thread对象的用法相同,也有start......
  • C++11手写线程池1
    线程池结构  任务队列结构体 保存一个回调函数指针和一个,参数指针 实现任务队列 为了多个生产者多个消费者取东西混乱的避免要加互斥锁线程池threadpool类要实现的初始化一个线城池参数是最小数和最大数   malloc和new的区别new要调用该类的构......
  • java实现大文件多线程上传案例
    当机器内存大小为4G,需要上传一个大小为50G的文件时,为了避免内存溢出,可以采用分片上传的方式,即将大文件切分成多个小片段进行并发上传。以下是一个详细的方案和代码实现示例:方案说明:将大文件切分成多个大小适当的片段(例如每个片段大小为100MB)。创建一个线程池来管理并发上传任务,......
  • 16核22线程!酷睿Ultra 9 185H曝光:英特尔革命性架构转变
    在近日的英特尔ON技术创新峰会上,英特尔公布了代号为MeteorLake的第一代酷睿Ultra处理器的最新消息。据英特尔介绍,该处理器将于今年12月14日正式上市,预计搭载酷睿Ultra移动处理器的首批笔记本将在今年底首发,并且在春节前后大量出货。和经典的i3、i5、i7以及i9定位类似,第一代酷......
  • Linux之线程池
    前备知识:互斥锁、条件变量。前面博客有相关知识测试代码。基本思想就是新建若干个线程,统一管理。当需要插入一个任务时,则在线程池里面找一个空闲线程来执行任务。若线程池的所有线程都有任务时,则让任务等待,等有新的线程空闲下来再执行。示例是线程池开辟10个线程,每个线程处理一......
  • 如何使用Linux进行进程调度优化
    如何使用Linux进行进程调度优化WBOY发布:2023-08-0216:13:21原创315人浏览过 如何使用Linux进行进程调度优化随着计算机技术的发展和互联网的普及,对计算机系统的性能要求越来越高。进程调度作为操作系统的重要功能之一,在提高系统性能方面起着重要作用。Lin......
  • Linux内核进程管理与调度:策略优化与实践分析
    Linux内核进程管理与调度:策略优化与实践分析原创 李斌 嵌入式悦翔园 2023-05-0611:40 发表于上海关注★星标公众号,第一时间获取信息嵌入式悦翔园本公众号专注于嵌入式技术,包括但不限于STM32、Arduino、51单片机、物联网、Linux等编程学习笔记,同时,公众号内包含大量......