前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多线程与多进程

多线程与多进程

作者头像
@小森
发布2024-03-15 11:46:28
720
发布2024-03-15 11:46:28
举报
文章被收录于专栏:xiaosenxiaosen

操作系统比如 Mac OS X,Linux,Windows 等,都是支持“多任务”的操作系统,操作系统可以同时运行多个任务。一边在逛淘宝,一边在听音乐,一边在用微信聊天,这就是多任务,至少同时有 3 个任务正在运行。

进程和线程都是操作系统中的重要概念。

对于一般的程序,可能会包含若干进程;而每一个进程又可能包含多个同时执行的线程。进程是资源管理的最小单位,而线程则是程序执行的最小单位。

进程和线程的概念

一个任务就是一个进程(Process),比如打开一个浏览器就是启动 一个浏览器进程,打开记 事本就启动了一个记事本进程。

进程:进程就是正在执行的程序,为多任务操作系统中执行任务的基本单元,是包含了程序指令和相关资源的集合。

线程:线程是进程的执行单元。对于大多数程序来说,可能只有一个主线程。但是,为了能够提高效率,有些程序会采用多线程,在系统中所有的线程看起来都是同时执行的。

进程和线程的对比

进程是重量级的,具体包括进程映像的结构、执行细节以及进程间切换的方法。在进程中,需要处理的问题包括进程间通信、临界区管理和进程调度等。

线程刚好相反,它是轻量级的。线程之间共享许多资源,容易进行通信,生成一个线程的开销较小。但是使用线程会有死锁、数据同步和实现复杂等问题。

由于Python使用了全局解释器锁(GIL)和队列模块,其在线程实现的复杂度上相对于其他语言来说要低得多。  

 进程的开发

模块

介绍

模块

介绍

os/sys

包含基本进程管理函数

subprocess

Python基本库中多进 程编程相关模块

multiprocessing

Python基本库中多进程编程模块,核心是fork,重开一个进程,首先会把父进程的代码copy重载一遍

threading

Python基本库中多线 程管理相关模块

multiprocessing模块

multiprocessing 是一个用与 threading 模块相似API的支持产生进程的包。 

代码语言:javascript
复制
from multiprocessing import Process
import os
from time import sleep, time
def test1(name):
 
  print("当前进程的ID", os.getpid())
  print("父进程的ID", os.getppid())
  print("当前进程的名字:", name)
  # 休息3秒
  sleep(3)

if __name__ == '__main__':
  start = time()
  # 创建多个子进程,并且把这些子进程放入列表中
  process_list = []
  print("主进程的ID", os.getpid())
  for i in range(10):
    # args:表示被调用对象的位置参数元组,这里Process就是属于Process类
    p = Process(target=test1, args=('process-%s' % i,))
    
    p.start()
    process_list.append(p)

或者自定义一个Process进程类,该类中的run函数由一个子进程调用执行

代码语言:javascript
复制
from multiprocessing import Process
import os
from time import sleep, time
# 自定义一个进程类 继承Process类
class MyProcess(Process):
    def __init__(self, name):
        Process.__init__(self)
        self.name = name
    def run(self):
        print("当前进程的ID", os.getpid())
        print("父进程的ID", os.getppid())
        print("当前进程的名字:", self.name)
        sleep(3)
if __name__ == '__main__':
  print("主进程ID", os.getpid())
  # 返回当前时间的时间戳
  start = time()
  process_list = []
  for i in range(10):
    # args:表示被调用对象的位置参数元组
    p = MyProcess("process-%s" % i)
    # 开始进程
    p.start()
    process_list.append(p)
  for p in process_list:
    # 一般都会需要父进程等待子进程结束再执行父进程后面的代码,需要join,等待所有的子进程结束
    p.join()
    
    end = time() - start
    print(end)

进程间的通信

Python 提供了多种实现进程间通信的机制,主要有 2 种: 1. Python multiprocessing 模块下的 Queue 类,提供了多个进程之间实现通信的诸多方法 2. Pipe,又被称为“管道”,常用于实现 2 个进程之间的通信,这 2 个进程分别位于管道的两端 

Queue 实现进程间通信:

需要使用 multiprocessing 模块中的 Queue 类。简单的理解 Queue 实现进程间通信的方式,就是使用了操作系统给开辟的一个队列空间,各个进程可以把数据放到该队列中,当然也可以从队列中把自己需要的信息取走。 

Pipe 实现进程间通信:

1、send(obj) 发送一个 obj 给管道的另一端,另一端使用 recv() 方法接收。需要说明的是,该 obj 必须是可序列化的,如果该对 象序列化之后超过 32MB,则很可能会引发 ValueError 异常。 2、recv() 接收另一端通过 send() 方法发送过来的数据 3、close() 关闭连接 4、poll([timeout]) 返回连接中是否还有数据可以读取 5、send_bytes(buffer[, offset[, size]])  发送字节数据。如果没有指定 offset、size 参数,则默认发送 buffer 字节串的全部数据;如果指定了 offset 和 size 参数,则只发送 buffer 字节串中从 offset 开始、长度为 size的字节数据。通过该方法发送的数据,应该使用 recv_bytes() 或 recv_bytes_into 方法接收。 6、recv_bytes([maxlength]) 接收通过 send_bytes() 方法发送的数据,maxlength 指定最多接收的字节数。该方法返回接收到的字节数据 7、recv_bytes_into(buffer[, offset]) 功能与 recv_bytes() 方法类似,该方法将接收到的数据放在 buffer 中

 进程池Pool

Python 提供了更好的管理多个进程的方式,就是使用进程池。进程池可以提供指定数量的进程给用户使用,即当有新的请求提交到进程池中时,如果池未满,则会创建一个新的进程用来执行该请求;反之,如果池中的进程数已经达到规定最大值,那么该请求就会等待,只要池中有进程空闲下来,该请求就能得到执行。 

Pool中的函数说明:

  • Pool(12):创建多个进程,表示可以同时执行的进程数量。默认大小是CPU的核心数果。
  • join():进程池对象调用join,会等待进程池中所有的子进程结束完毕再去结束父进程。
  • close():如果我们用的是进程池,在调用join()之前必须要先close(),并且在close()之后不能再继续往进程池添加新的进程 。
  • pool.apply_async(func,args,kwds) : 异步执行 ;将事件放入到进程池队列 。args以元组的方式传参,kwds以字典的方式传参。
  • pool.apply_sync(func,args,kwds):同步执行;将事件放入到进程池队列。

线程的开发 

使用threading模块来创建线程是很方便的。只要将类继承于threading.Thread,然后在init方法中调用threading.Thread类中的init方法,重写类的run方法就可以了。

单线程执行: 

代码语言:javascript
复制
import time
def say():
  print("这是单线程,单线程,单线程!")
  time.sleep(1)
if __name__ == "__main__":
  start = time.time()
  for i in range(5):
    say()
  end = time.time()
  print(f'使用时间:{end - start}')

 多线程执行:

代码语言:javascript
复制
import threading
import time
def say():
  print("多线程,多线程,多线程")
  time.sleep(1)
if __name__ == "__main__":
  
  start = time.time()
  for i in range(5):
    
    t = threading.Thread(target=say)
    t.start()  
  end = time.time()
  print(f'使用时间:{end - start}')

 join方法

该方法将等待,一直到它调用的线程终止. 它的名字表示调用的线程会一直等待,直到指定的线程加入它,当一个进程启动之后,会默认产生一个主线程,因为线程是程序执行流的最小单元,当设置多线程时,主线程会创建多个子线程,在python中,默认情况下(其实就是setDaemon(False)),主线程执行完自己的任务以后,就退出了,此时子线程会继续执行自己的任务,直到自己的任务结束。而join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程在终止。

setDaemon 

将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。有时候我们需要的是 只要主线程完成了,不管子线程是否完成,都要和主线程一起退出, 这时就可以 用setDaemon方法 。

代码语言:javascript
复制
from threading import Thread
import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")
def bar():
    print(456)
    time.sleep(3)
    print("end456")
t1 = Thread(target=foo)
t2 = Thread(target=bar)
t1.daemon = True
t1.start()
t2.start()
print("main-------")

threading模块中Thread类的常用方法:

 线程间的通信

为了避免业务开启过多的线程时。我们就可以通过信号量Semaphore来设置指定个数的线程。车站有3 个安检口,那么同时只能有3 个人安检,别人来了,只能等着别人安检完才可以过。

代码语言:javascript
复制
from threading import Thread, BoundedSemaphore
from time import sleep
def an_jian(num):
      semapshore.acquire()
      print('第{}个人安检完成!'.format(num))
      sleep(2)
      semapshore.release()
if __name__ == '__main__':
    semapshore = BoundedSemaphore(3)
    for i in range(20):
        thread = Thread(target=an_jian, args=(i,))
        thread.start()
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-03-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 进程和线程的概念
  •  进程的开发
  • 线程的开发 
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档