python详解(8)--python中的多任务(进程与线程)

本文深入讲解了Python中的多任务处理,包括多线程和多进程。阐述了线程与进程的区别,如线程共享进程资源,进程间资源独立。在Python中,多线程通过`threading`模块实现,多进程通过`multiprocessing`模块实现。文章还介绍了线程同步机制,如互斥锁、信号量、条件变量等,并讨论了进程间通信的方法,如队列、管道等。最后,讨论了计算密集型和IO密集型任务的处理策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多任务:指的是一台电脑可以同时运行多个应用程序(一个应用程序可能有多个进程),是一种共享CPU的方法。

协同式多任务(cooperative multitasking):进程在执行过程中不受限制的占用cpu,不存在时间片概念,系统对cpu使用权的收回要靠进程主动上交。

抢先式多任务(preemptive multitasking):当一个新的进程开始时,在它的时间片(timeslice)之内,cpu的使用权是在这个进程手里的,当时间片结束时,系统要收回cpu的使用权做下一轮分配,即由系统进行cup使用权的分配与应用程序无关。

单核:CPU集成了一个运算核心,所有的任务都是由这个运算核心完成的,同一时间只能完成一个任务。

多核:CPU集成了多个运算核心,可以同一时间让多个运算核心同时工作完成多个任务。

并发:任务数多于CPU核心数,通过操作系统调度(Scheduling)方法,在很短时间内不断切换(时间片轮转,并有优先级算法等以任务的重要度优先考虑),实现在固定时间内多个任务共同执行。

并行:任务数小于CPU核心数,多个任务执行于不同的运算核心,真正的同时执行。

程序(program):是一种静态的实体,如封装的EXE文件,.py文件等。

进程(process):可以认为是运行的程序,是一个动态的实体,代表程序的执行过程,随着程序中指令的执行而不断的变化,在某个特定时刻的进程的内容被称为进程映像(process image),分为①正文段(text,被执行的机器指令,即代码)②用户数据段(user segment,存放进程在执行时直接操作的所有数据,包括进程使用的全部变量在内)③系统数据段(system segment,存放程序运行的环境,即进程的控制信息,是进程与程序的区别所在);进程有三种状态,就绪态、执行态、等待态(堵塞态),是多任务的一种实现方式。

线程(thread):线程(有时候称为轻量级进程)与进程类似,不过它们是在同一个进程下执行的,并共享相同的上下文,即将多任务的思想拓展到应用层面,将单个任务分解为小任务,再将这些任务分配给不同的CPU内核,提高进程速度;以python程序为例,其主代码顺序执行即主线程,在主线程过程中有一些函数、类等也需要新的线程去执行,称之为子线程。是多任务的一种轻量级的实现方式。

一、python中的多线程

python中的threading模块
python内置了threading模块(其前身为thread模块,但相对底层,在python3中更名为_thread),可用于多线程的执行,threading模块的类对象如下:

Thread	表示一个执行线程的对象
Lock	锁对象
RLock	可重入锁对象,使单一线程可以(再次)获得已持有的锁(递归锁)
Condition	条件变量对象,使得一个线程等待另外一个线程满足特定的条件,比如改变状态或者某个数据值
Event 	条件变量的通用版本,任意数量的线程等待某个事件的发生,在该事件发生后所有的线程都将被激活
Semaphore	为线程间的有限资源提供一个计数器,如果没有可用资源时会被阻塞
BoundedSemaphore	与Semaphore相似,不过它不允许超过初始值
Timer	与Thread类似,不过它要在运行前等待一定时间
Barrier	创建一个障碍,必须达到指定数量的线程后才可以继续

threading.enumerate() 返回一个列表,列表中是当前的线程,其中主线程显示为MainThread
threading.current_thread() 返回当前线程,线程名在定义线程时指定,若不指定系统会默认写为Thread-1等,除了打印时用于显示没有别的用途

其中Thread类是线程的主要执行对象,其他类都属于同步机制。

# Thread类属性
name	线程名
ident	线程的标识符
daemon	布尔值,表示这个线程是否是守护线程

# Thread类的主要方法
__init__(group=None,target=None,name=None,args=(),kwargs={},verbose=None,daemon=None)	实例化一个线程对象,需要一个可调用的target对象,以及参数args或者kwargs。还可以传递name和group参数。daemon的值将会设定thread.daemon的属性
start()	开始执行该线程
run()	定义线程的方法(一般开发者在子类中重写此方法)
join(timeout=None)	直至启动的线程终止之前一直挂起;除非给出了timeout(单位秒),否则一直被阻塞

在python中使用多线程的常用方法有两种:
(1)创建Thread类实例,传递函数作为线程执行的内容。

def loop():
    n = 0
    while n < 5:
        n = n + 1
        print(threading.current_thread().name)
        time.sleep(1)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('end')

如上所示,直接生成Thread实例,调用start()方法即开始执行线程,join()方法意为等待子线程执行完毕后再执行主线程,否则'end'会在loop函数调用前就打印出来。

(2)派生Thread的子类,并创建子类的实例。

class MyThread(threading.Thread):
    def __init__(self,func,args,name=''):
        threading.Thread.__init__(self)
        self.func = func
        self.name = name
        self.args = args
        
    def run(self):
        print('开始执行',self.name,' 在:',ctime())
        self.res = self.func(*self.args)
        print(self.name,'结束于:',ctime())
        
    def getResult(self):
        return self.res

如上所示,自定义Thread的派生类,在其中定义run方法(一般在run方法中会直接调用start()/join()方法),其实只是上述方法的封装,但更适合面向对象的开发。
:①主线程是可能在子线程之前结束的,但其并不会直接将子线程结束掉,join()只有在主线程需要等待子线程完成时候才是有用的(主线程与子线程是相互独立的,其都同属于一个进程,在默认情况下,进程会等待所有的子线程结束后再结束);
②只有调用实例的start方法时,才会创建并开始线程的执行;
③假如在一个函数中使用线程并使用变量传入参数,若子线程函数需要等待,而主线程很快的执行完毕,由于主线程执行完毕后与它关联的所有数据都会被回收,则子线程的参数就不存在了(正常情况下子线程会自动关闭,但特殊情况下其会报错)。

python多线程的同步机制

多线程与全局变量:在一个进程的多线程任务中,多个线程共享全局变量(创建Thread类实例时传入的变量就是全局变量,或处理磁盘上的文件等),即在一个线程中声明或更改了全局变量,按时间顺序另一个线程会获得更改后的全局变量。注意,在共享全局变量时,多个线程不断调用全局变量并且不断改变,可能会产生全局变量在不同进程中同时

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值