Python学习----进程和线程

并发:在一段时间内,交替执行多个任务
例如:对于单核CPU处理多任务,操作系统轮流让各个任务交替执行,假如:软件1执行0.01s,切换到软件2,软件2执行0.01s,再切换到软件3,执行0.01s,这样反复执行下去。

并行:在一段时间内,真正的同时一起执行多个任务
例如:对于多核CPU,操作系统会给CPU的每个内核安排一个执行任务(任务数小于或等于CPU核心数)

进程介绍:
在Python中,想要实现多任务可以使用进程来完成
进程:是计算机资源分配的最小单位,它是操作系统进行资源分配和调度运行的基本单位,通俗理解:一个正在运行的程序就是一个进程。

创建进程的步骤

1、导入进程包
import multiprocessing
2、通过进程类创建进程对象
进程对象 = multiprocessing.Process()
Process 方法参数说明
target 执行的目标任务名,这里指的是函数名
name 进程名,一般不设置
group 进程组,目前只能使用None
3、启动进程执行任务
进程对象.start()

import multiprocessing
import time


def coding():
    for i in range(3):
        print("coding....")
        time.sleep(0.2)


def music():
    for i in range(3):
        print("music....")
        time.sleep(0.2)


if __name__ == '__main__':
    coding_process = multiprocessing.Process(target=coding)
    music_process = multiprocessing.Process(target=music)
    coding_process.start()
    music_process.start()
进程执行带有参数的任务

args 以元组的方式给执行任务传参
kwargs 以字典的方式给执行任务传参
当元组只有一个参数时,需要使用 逗号 结尾,以区分它是元组还是小括号。多个参数时,按照顺序对应赋值给形参。
字典传参时,key值必须和函数的形参一致

def coding(num):
    for i in range(num):
        print("coding....")
        time.sleep(0.2)


def music(count):
    for i in range(count):
        print("music....")
        time.sleep(0.2)


if __name__ == '__main__':
	#当元组只有一个参数时,需要使用 逗号 结尾,以区分它是元组还是小括号
    coding_process = multiprocessing.Process(target=coding, args=(3,))
    #字典传参时,key值必须和函数的形参一致
    music_process = multiprocessing.Process(target=music, kwargs={"count": 2})
    coding_process.start()
    music_process.start()
获取进程编号

当程序中进程的数量越来越多,就无法区分主进程和子进程以及不同的子进程,为了方便管理,每个进程都有自己的编号。通过编号区分不同的进程。

首先导入os模块,直接通过模块中的os对象获取
import os

1、获取当前进程的编号
os.getpid()
2、获取当前进程的父进程编号
os.getppid()

在这里插入图片描述

进程间不共享全局变量

实际上,创建一个子进程就是把主进程的资源进行拷贝产生一个新的进程,这里主进程和子进程时相互独立的。
在这里插入图片描述

主进程和子进程的结束顺序

主进程会等待所有子进程执行结束再结束。
在这里插入图片描述

打印显示主进程结束,但是主进程并没有真的结束,最下面显示 Process finished 才代表主进程结束。这就说明了主进程会等待子进程结束之后才会结束。

方法一:设置守护主进程,来实现主进程结束之后子进程也直接销毁
在这里插入图片描述
方法二:手动销毁子进程
在主进程结束之前,手动销毁子进程

import multiprocessing
import time


def work():
    for i in range(10):
        print("工作中...")
        time.sleep(0.2)
    print("子进程结束")


if __name__ == '__main__':
    work_process = multiprocessing.Process(target=work)
    work_process.start()
    # 主进程休息1s ,假装执行其他工作
    time.sleep(1)
    # 手动销毁子进程
    work_process.terminate()
    print("主进程结束")

实现多任务的另一种方式----线程

在Python中,想要实现多任务还可以使用多线程来完成

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

线程是程序执行的最小单位,实际上,进程只负责分配资源,而利用这些资源执行程序的是线程,也就说进程是线程的容器,一个进程中最少有一个线程来负责执行程序,同时线程自己不拥有系统资源,只需要一点在运行中必不可少的资源(这些资源用完就会归还,不会一致占用)。但它可与同属一个进程的其他线程共享进程的所有资源。这就像通过一个QQ软件,你和两个人聊QQ,只需要打开两个窗口。
线程实现多任务的同时也节约了资源。

线程的创建步骤

1、导入线程模块
import threading
2、通过线程类创建线程对象
线程对象 = threading.Thread(target = 任务名)
target 执行的目标任务名,这里指的是函数名(方法名)
name 线程名,一般不用设置
group 线程组,目前只能使用None
3、启动线程执行任务
线程对象.start()

在这里插入图片描述

线程执行带有参数的任务

和进程一样
args 以元组的方式给执行任务传参
同样,元组只有一个元素需要逗号结尾,多个形参按照参数顺序进行形参赋值
在这里插入图片描述
kwargs 以字典方式给执行任务传参
同样,字典的key值需要和任务的形参名保持一致在这里插入图片描述

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

对比进程,主线程会等待所有子线程结束后,主线程再结束

设置主线程结束后,子线程也结束
方法一:设置守护主线程
只需要在创建线程时设置:daemon=True 即可。

coding_threading = threading.Thread(target=coding,daemon=True)

方法二:同样设置,不过是通过函数设置,上面是初始化参数设置
子线程对象.setDaemon(True)

coding_threading.setDaemon(True)
线程间的执行顺序

线程之间的执行顺序是无序的,他们之间顺序是通过CPU的调度决定的
代码演示:
通过threading.current_thread()获取线程信息。在sleep时,可能CPU被其他线程获取执行权,每次所以打印顺序不同。证明了线程之间的执行顺序是无序的,通过CPU调度决定
在这里插入图片描述

线程之间共享全局变量

这点和进程不同,进程是将所有资源拷贝一份到自己进程中。
线程:多个线程都是在同一个进程中,多个线程使用的资源都是同一个进程中的资源,因为多线程之间是共享全局变量的。
代码演示:

import threading
import time

# 全局变量
my_list = []

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

def read_data():
    print("read:", my_list)

if __name__ == '__main__':
    write_thread = threading.Thread(target=write_data)
    read_thread = threading.Thread(target=read_data)

    write_thread.start()
    time.sleep(0.5)  # 因为线程执行无序,可能还没写完就读了,所以保证读取是在写入之后
    read_thread.start()

在这里插入图片描述

线程之间共享全局变量数据出现错误问题
import threading

# 全局变量
g_num = 0


def sum_num1():
    global g_num
    for i in range(10000000):
        g_num += 1
    print("sum1:", g_num)


def sum_num2():
    global g_num
    for i in range(10000000):
        g_num += 1
    print("sum2:", g_num)


if __name__ == '__main__':
    sum1_threading = threading.Thread(target=sum_num1)
    sum2_threading = threading.Thread(target=sum_num2)
    sum1_threading.start()
    sum2_threading.start()

线程之间全局变量是共享的。
sum1 线程从 g_num获取值为0,进行加1操作,然后再将1赋值给全局变量g_num,如果sum1线程在将赋值给全局变量这步时,失去了CPU的执行权,sum2线程获取全局变量的值仍然为0,线程sum2执行加1操作。并将加1后的值赋值给全局变量。这时两个线程都处于将值返回给全局变量的过程中,那么最后的结果就是,进行两次加1操作,但是,全局变量的值仍然为1。

解决方案
方案一: 线程同步
保证在同一时刻只能有一个线程去操作全局变量(例如上面代码,只能在线程进行加1操作并且赋值给全局变量之后,其他线程才能获取到全局变量的值)
通过互次锁实现
互次锁:对共享数据进行锁定,保证同一时刻只有一个线程去操作。多个线程同时抢锁,抢到锁的线程先执行,没有抢到锁的线程进行等待。等锁释放完之后,其他线程再去抢这个锁。

使用互次锁:
1、互次锁的创建
mutex = threading.Lock()
2、上锁
mutex.acquire()
3、释放锁
mutex.release()
演示代码:

import threading

# 全局变量
g_num = 0

def sum_num1():
    # 上锁
    mutex.acquire()
    global g_num
    for i in range(10000000):
        g_num += 1
    print("sum1:", g_num)
    # 解锁
    mutex.release()

def sum_num2():
    mutex.acquire()
    global g_num
    for i in range(10000000):
        g_num += 1
    print("sum2:", g_num)
    mutex.release()

if __name__ == '__main__':
    # 创建锁
    mutex = threading.Lock()

    sum1_threading = threading.Thread(target=sum_num1)
    sum2_threading = threading.Thread(target=sum_num2)
    sum1_threading.start()
    sum2_threading.start()

mutex就是锁,但是注意,想要实现串行化,必须使用的是同一把锁。
这样虽然不会出现数据错误问题,但是这样效率低,和单线程差不多。

进程和线程的区别

1、关系对比
     1)、线程是依附在进程里面的,没有进程就没有线程
     2)、一个进程默认提供一条线程

2、区别对比
    1)、进程之间不共享全局变量
    2)、线程之间共享全局变量,但是要注意资源竞争问题,解决办法:互次锁或者线程同步
    3)、创建进程的开销比创建线程大
    4)、进程是资源分配的基本单位,线程是CPU调度的基本单位
    5)、线程不能独立执行,必须依附在进程中

3、优缺点对比
     进程优缺点:
     优点:可以使用多核 缺点:资源开销大
     线程优缺点:
     优点:资源开销小 缺点:不能使用多核
一个进程只能使用到一个内核,所以线程之间只能争夺一个内核(CPU)执行权

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值