Python进阶(一)

1.多任务

 多任务处理是指在同一时间内执行多个任务或运行多个应用程序的能力。 

 多任务处理通常分为两种类型:

  1. 并发多任务处理:这是一种在一段时间内交替去执行任务的方法。尽管多个任务在交替执行,但给用户的感觉是它们都在同时运行。例如,在单核心CPU的计算机上,操作系统通过快速切换不同的任务来模拟多任务处理的效果。
  2. 并行多任务处理:这是真正意义上的多任务同时执行。它通常要求系统具有多个处理器或处理器核心,每个处理器或核心可以同时运行一个任务。在多核心CPU的计算机上,操作系统可以为每个核心分配一个任务,从而实现真正的并行处理。

2.进程

        进程是系统进行资源分配的基本单位,也是操作系统结构的基础。 简单来说就是一个正在运行的程序就是一个进程。  

    进程的创建步骤
    1.导入进程包:import multiprocessing
    2.通过进程类创建进程对象:进程对象=multiprocessing.Process()
    3.启动进程执行任务:进程对象.start()
    在创建进程对象时:进程对象=multiprocessing.Process()需要输入参数;
    参数           说明
    target    执行的目标任务名,函数名(方法名)
    name      进程名,一般不设置
    group     进程组,目前只能用None
import multiprocessing
def func(name,num):
    print(f"i am {name},i am process {num}")
if __name__ == '__main__':
    process=[]
    for i in range(0,5):
        p=multiprocessing.Process(target=func,args=('python',i))
        process.append(p)
        p.start()
    print(process)
    for p in process:
        p.join()
    print("finished")

进程执行带有参数的任务
参数名          说明
args         以元组的方式给执行任务传参
kwargs       以字典的方式给执行任务传参
def sing(num):
    for i in range(num):
        print("sing...")
        time.sleep(0.5)
def dance(num):
    for i in range(num):
        print("dance...")
        time.sleep(0.5)
if __name__ == '__main__':
    sing_process=multiprocessing.Process(target=sing,args=(3,))
    dance_process=multiprocessing.Process(target=dance,kwargs={'num':3})
    sing_process.start()
    dance_process.start()
当程序中的进程越来越多,如果无法区分主进程和子进程,那么就无法进行有效的进程管理,所以每个进程都是有编号的。当程序执行起来,系统就会会程序分配一个主进程。
获取当前进程编号:os.getpid()
获取当前父进程编号:os.getppid()
父进程就是创建了这个进程的进程
注意:
主进程会等待所有子进程结束才结束
设置守护主进程:
子进程对象.daemon=True
一旦主进程结束,子进程也跟着结束
multiprocessing模块在Python中提供了创建、管理和同步多个进程的功能。
以下是一些multiprocessing模块中常用的方法:

Process类:
Process(target=run_func, args=(), kwargs={}, *, name=None, daemon=None, group=None, exitcode=None, authkey=None, pid=None, start_method=None, context=None, initializer=None, initargs=()): 创建一个进程对象。target参数是目标函数,args和kwargs是传递给目标函数的参数。

start(): 启动进程。

join([timeout]): 等待进程结束。如果指定了timeout,则最多等待timeout秒。

is_alive(): 检查进程是否还活着。

terminate(): 强制终止进程。

name: 进程的名称。

pid: 进程的PID(进程ID)。

exitcode: 进程的退出代码。
进程池(Pool):
Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]]): 创建一个进程池对象。processes参数指定进程池中的进程数。

apply(func[, args[, kwargs]]): 调用函数并阻塞,直到结果返回。

apply_async(func[, args[, kwargs[, callback[, error_callback]]]]): 调用函数,返回一个AsyncResult对象,你可以使用它来获取结果或检查状态。

map(func, iterable[, chunksize]): 使用进程池中的多个进程对可迭代对象中的每个元素应用函数,并返回结果列表。

map_async(func, iterable[, chunksize[, callback[, error_callback]]]): 与map()类似,但返回一个AsyncResult对象。

close(): 关闭进程池,防止进一步的提交任务。

terminate(): 立即停止所有工作进程,不执行任何清理或完成正在执行的任务。

join(): 等待所有工作进程退出。
队列(Queue):
Queue([maxsize]): 创建一个队列对象,用于进程间通信。maxsize是队列的最大大小。

put(item[, block[, timeout]]): 将一个元素添加到队列中。如果队列已满且block为True,则等待可用空间(或直到超时)。

get([block[, timeout]]): 从队列中获取一个元素。如果队列为空且block为True,则等待可用元素(或直到超时)。

put_nowait(item): 不等待空间可用,立即尝试添加一个元素到队列中。如果队列已满,则引发异常。

get_nowait(): 不等待元素可用,立即尝试从队列中获取一个元素。如果队列为空,则引发异常。

qsize(): 返回队列中的元素数量(可能不准确)。

empty(): 如果队列为空,则返回True。

full(): 如果队列已满,则返回True。

close(): 关闭队列,阻止进一步的添加操作。

join(): 等待队列中的所有任务完成。
3.线程
在python中想要实现多任务还可以使用多线程来完成。
为什么要使用多线程呢?
进程是程序执行的最小单位,实际上进程只负责分配资源,
创建一个进程就会分配一定的资源,而利用这些资源执行程序的是线程。
进程是线程的容器,一个进程中最少有一个线程来负责执行程序,
同时线程自己不拥有系统资源,但它可以同属一个进程的其它线程共享进程所拥有的全部资源。




使用线程(threading)在编程中有多重原因,
尤其是在Python这样的语言中,尽管存在全局解释器锁(GIL)这样的限制。
以下是一些使用线程的主要原因:
并发性(Concurrency):
线程允许程序并发地执行多个任务,即在同一时间段内处理多个任务。
这对于响应性要求高的应用程序(如GUI应用、网络服务器等)尤为重要,
因为它们需要同时处理多个用户请求或事件。
资源共享:
线程共享其所属进程的内存空间,
包括代码段、数据段、打开的文件、信号处理器、当前环境变量和进程间通信的资源等。
这种共享使得线程间的通信和数据共享变得简单且高效。
开销较小:
与进程相比,线程的创建和销毁通常更加快速,
因为不需要为线程分配和回收系统资源(如内存、文件描述符等)。
这使得线程更适合用于需要频繁创建和销毁任务的情况。
响应性:
线程允许程序在等待一个任务完成的同时继续执行其他任务。
这对于需要保持持续响应性的应用程序来说非常有用,例如实时数据处理或用户交互。
异步I/O:
线程可以处理异步I/O操作,如网络请求或文件读写。
当线程在等待I/O完成时,它可以被挂起,以便其他线程可以使用CPU。
这提高了程序的总体吞吐量和效率。
利用多核CPU:
尽管Python的GIL限制了纯Python代码的多线程并行执行,
但你可以使用多进程(如通过multiprocessing模块)
或特定的Python扩展(如NumPy、Cython等)来利用多核CPU。
此外,对于I/O密集型任务,多线程仍然可以通过并发地执行多个I/O操作来间接地利用多核CPU。
简化编程模型:
线程提供了一种相对简单的编程模型,使得开发者可以更容易地理解和实现并发性。
与进程相比,线程之间的切换和通信通常更加直接和高效。
在一个程序运行中,操作系统会分配资源创建一个进程,这个进程中的线程称为主线程。

线程的创建步骤:
1.导入线程模块
import threading
2.通过线程类创建线程对象
线程对象=threading.Thread(target=任务名)
3.启动线程执行任务
线程对象.start()

在创建线程对象时参数如下:
参数名         说明
target       执行的目标任务名,指的是函数名
name         线程名,一般不用设置
group        线程组,None

import threading
import time

def sing():
    for i in range(3):
        print("sing...")
        time.sleep(1)
def dance():
    for i in range(3):
        print("dance...")
        time.sleep(1)



if __name__ == '__main__':
    sing_th = threading.Thread(target=sing)
    dance_th = threading.Thread(target=dance)
    sing_th.start()
    dance_th.start()
线程执行有参数的任务:
参数名       说明
args      以元组的方式给执行任务传参
kwargs    以字典的方式给执行任务传参,key要与任务的参数名一致
import threading
import time

def sing(num):
    for i in range(num):
        print("sing...")
        time.sleep(1)
def dance(num):
    for i in range(num):
        print("dance...")
        time.sleep(1)



if __name__ == '__main__':
    sing_th = threading.Thread(target=sing,args=(3,))
    dance_th = threading.Thread(target=dance,kwargs={'num':3})
    sing_th.start()
    dance_th.start()
主线程和子线程的结束顺序:
主线程会等待所有子线程结束后再结束。
要想主线程不等待子线程执行完毕再结束可以设置守护主线程。
1.
work_threading=threading.Thread(target=work,daemon=True)
2.
work_threading.setDaemon(True)

线程间的执行顺序:

线程之间的执行是无序的。

work_th=threading.current_thread()
获取当前线程的信息

 进程与线程的对比:

1.关系对比:

线程是依附在进程里面的,没有进程就没有线程。

一个进程默认提供一条线程,进程可以创建多个线程。

2.区别对比:

1.创建进程的资源开销大于线程

2.进程是操作系统分配资源的基本单位,线程是cpu调度的基本单位。

在Python中,选择使用进程(process)还是线程(thread)取决于你的具体需求和应用场景。

使用线程的情况:
I/O密集型任务:如果你的程序大部分时间都在等待I/O操作(如网络请求、文件读写等),
那么使用线程可能是一个好选择。Python的全局解释器锁(GIL)会阻止多个线程同时执行Python字节码,
但I/O操作通常不会受到GIL的影响,因为I/O操作会释放GIL,允许其他线程运行。
需要共享内存:线程共享同一个进程的内存空间,因此它们可以轻松地访问和修改共享数据。
然而,这也需要额外的同步机制来避免数据竞争和不一致。
计算量较小的任务:由于GIL的存在,Python中的线程并不适合执行计算密集型的任务。
但是,如果你的任务计算量较小,或者你可以通过优化来减少GIL的影响
(例如,使用内置函数或C扩展),那么线程可能仍然是一个可行的选择。

使用进程的情况:
计算密集型任务:由于Python的GIL,单个Python进程通常不能充分利用多核CPU的并行计算能力。
在这种情况下,使用多个进程可以更好地利用硬件资源。
需要隔离性:进程之间的内存是隔离的,这意味着一个进程中的错误(如内存泄漏或崩溃)
不会影响其他进程。这在处理不稳定的代码或需要与外部系统交互时非常有用。
利用多核CPU:如果你的程序需要并行处理大量任务,并且这些任务可以独立运行,
那么使用多个进程可以更好地利用多核CPU的并行计算能力。
需要避免GIL的影响:如果你正在使用Python的某些扩展模块(如NumPy、SciPy等),
这些模块可能已经释放了GIL,允许并行计算。
在这种情况下,使用进程可以确保充分利用这些模块的性能优势。

注意事项:
同步和通信:线程之间可以通过共享内存进行通信,
但也需要额外的同步机制来避免数据竞争和不一致。
进程之间的通信通常更加复杂,因为它们需要跨内存边界进行通信。
这可以通过管道、套接字、共享内存等方式实现。
开销:创建和管理进程的开销通常比线程大得多。
因此,在需要频繁创建和销毁任务的情况下,线程可能更加高效。


Python的GIL:Python的全局解释器锁(GIL)是Python线程模型中的一个重要概念。
它确保了在任何时候只有一个线程可以执行Python字节码。
这意味着在纯Python代码中,多线程并不能实现真正的并行计算。
然而,这个限制并不适用于所有类型的任务和所有Python扩展模块。

 

        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值