进程和线程

目录

1、进程

1.1、单进程

1.2、多进程

1.3获取进程编号

1.4进程执行带有参数的任务

1.5进程的注意点(重点:进程间不共享全局变量)

1.6守护主进程

进程总结:

2、线程

2.1、多线程的基本使用

2.2、多线程执行带有参数的任务

2.3、线程的注意点:

2.3.1、线程之间执行是无序的

 2.3.2、主线程会等待所有的子线程执行结束在结束

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

2.3.4、线程间共享全局变量问题

线程等待(join):

互斥锁



1、进程

1.1、单进程
import time
def dance():
    for i in range(5):
        time.sleep(1)
        print("跳舞", i)
​
def sing():
    for i in range(5):
        time.sleep(1)
        print("唱歌", i)
​
if __name__ == '__main__':
    dance()
    sing()

执行结果

跳舞 0
跳舞 1
跳舞 2
跳舞 3
跳舞 4
唱歌 0
唱歌 1
唱歌 2
唱歌 3
唱歌 4

单进程,需要10秒完成

最少有一个进程 这个进程中最少有一个线程

1.2、多进程

进程:在《运行》程序就是进程,进程是分配资源的最小单位

线程:使用资源的最小单位

Process进程类的说明

Process()

参数:

  • group:指定进程组,目前只能使用None

  • target:执行的目标任务名

  • name:进程名字
  • args:以元组的方式给执行任务传参
  • kwargs:以字典的方式给执行任务传参

Process创建的实例对象的常用方法:

  • start():启动子进程实例

  • join():等待子进程执行结束

  • terminate():不管任务是否完成,立即终止子进程

Process创建的实例对象的常用属性:

name:当前进程的别名,默认为Process-N,N为从1开始递增的整数

多进程完成多任务的代码:

多进程的使用流程:

(1)导入模块(multiprocesing)

(2)创建子进程(Process)

(3)开启子进程(start)

import time
import multiprocessing
def dance():
    for i in range(5):
        time.sleep(1)
        print("跳舞", i)
​
def sing():
    for i in range(5):
        time.sleep(1)
        print("唱歌", i)
​
if __name__ == '__main__':
    # 创建子进程
    # Procrss:
    # target: 指定执行的任务名(函数名)
    myDance = multiprocessing.Process(target=dance)
    mySing = multiprocessing.Process(target=sing)
    # 开启子进程(如果不开启是不会执行子进程的)
    myDance.start()
    mySing.start()

多进程 需要5秒完成(提升执行的效率)

注意点!!!

有三个进程:1个主进程,2个子进程

有三个线程:每个进程里有一个线程(执行任务的)

一旦程序开启,默认就会有一个主进程,主进程里默认就会有一个主线程

1.3获取进程编号

获取进程编号的目的:验证主进程和子进程的关系,可以得知子进程是由哪个主进程创建出来的(因为子进程需要主进程回收资源)

获取进程编号的两种操作

  • 获取当前进程编号 os.getpid()

  • 获取当前父进程编号 os.getppid()

获取当前进程编号:os.getpid()

单进程

import os
def dance():
    print("dance: ", os.getpid())
​
def sing():
    print("sing: ", os.getpid())
​
if __name__ == '__main__':
    print("主进程id: ", os.getpid())
    dance()
    sing()
运行结果
主进程id:  51264
dance:  51264
sing:  51264
    
    
id都相同,得出只有一个进程

多进程

import multiprocessing
import os
def dance():
    print("dance子进程id: ", os.getpid())
    print("dance父进程id: ", os.getppid())
​
def sing():
    print("sing子进程id: ", os.getpid())
    print("sing父进程id: ", os.getppid())
​
if __name__ == '__main__':
    print("主进程id: ", os.getpid())
    myDance = multiprocessing.Process(target=dance)
    mySing = multiprocessing.Process(target=sing)
    myDance.start()
    mySing.start()
​

运行结果

主进程id:  72076
dance子进程id:  21188
dance父进程id:  72076
sing子进程id:  43240
sing父进程id:  72076
1.4进程执行带有参数的任务
import os
import multiprocessing
# 带有参数的函数
def dance(count):
    for i in range(count):
        print("跳舞", i)
​
def sing(sum):
    print(sum)
​
if __name__ == '__main__':
    # args: 元组!!!  (单个元素的元组要有 逗号)
    # kwargs: 字典!!!(key值要和函数中的形参完全重名)
    myDance = multiprocessing.Process(target=dance, args=(5,))
    mySing = multiprocessing.Process(target=sing, kwargs={"sum": 5})
    myDance.start()
    mySing.start()

运行结果

跳舞 0
跳舞 1
跳舞 2
跳舞 3
跳舞 4
5
1.5进程的注意点(重点:进程间不共享全局变量)

进程间不共享全局变量

进程是分配资源的最小单位=====》每个进程(主进程,子进程)都会有自己独立的空间。

创建子进程会对主进程资源进行拷贝,也就是说子进程是主进程的一个副本。

之所以进程之间不共享全局变量,是因为操作的不是同一个进程里面的全局变量,只不过不同进程里面的全局变量名字相同而已。

import multiprocessing
import time
# 全局变量列表
globalNum = []
def myWrite():
    # 声明变量
    global globalNum
    # 向全局变量列表中写入数据
    for i in range(5):
        globalNum.append(i)
    print("myWrite方法中的globalNum:", globalNum)
​
​
def myRead():
    # 读取全局变量globalNum的值
    global globalNum
    print("myRead方法中的globalNum:", globalNum)
​
if __name__ == '__main__':
    mywrite = multiprocessing.Process(target=myWrite)
    myread = multiprocessing.Process(target=myRead)
    mywrite.start()
    time.sleep(1)   # 保证数据写入全局变量globalNum中
    myread.start()

运行结果

myWrite方法中的globalNum:[0, 1, 2, 3, 4]
myRead方法中的globalNum:[]
1.6守护主进程

注意!!!默认主进程会等待所有的子进程结束之后在结束

import multiprocessing
import time
def func():
    for i in range(5):
        time.sleep(0.2)
        print("func", i)
​
if __name__ == '__main__':
    # 程序一旦运行,就会默认创建主进程
    myFunc = multiprocessing.Process(target=func)   # 主进程创建了子进程
    myFunc.start()  # 主进程开启了子进程
    print("主进程over")

运行结果

主进程over
func 0
func 1
func 2
func 3
func 4
  • 为了保证子进程能够正常的运行,主进程会等待所有的子进程执行完以后在销毁,设置守护主进程的目的是主进程退出子进程销毁,不让主进程在等待子进程去执行

  • 设置守护主进程方式:子进程对象.daemon = True

  • 销毁子进程方式:子进程对象.terminate()

方式1:设置守护进程

import multiprocessing
import time
def func():
    for i in range(5):
        time.sleep(0.2)
        print("func", i)
​
if __name__ == '__main__':
    # 程序一旦运行,就会默认创建主进程
    myFunc = multiprocessing.Process(target=func)   # 主进程创建了子进程
    
    # 方式1:设置守护进程(注意:需要在start之前设置  否则无效)
    myFunc.daemon = True
    myFunc.start()  # 主进程开启了子进程
    time.sleep(1)
    print("主进程over")

方式2:手动设置

import multiprocessing
import time
def func():
    for i in range(5):
        time.sleep(0.2)
        print("func", i)
​
if __name__ == '__main__':
    # 程序一旦运行,就会默认创建主进程
    myFunc = multiprocessing.Process(target=func)   # 主进程创建了子进程
    myFunc.start()  # 主进程开启了子进程
    time.sleep(1)
    
    # 方式2:手动设置
    myFunc.terminate()
    print("主进程over")

运行结果

func 0
func 1
func 2
主进程over
进程总结:

1、多进程的使用流程

(1)导入模块(multiprocesing)

(2)创建子进程(Process)

(3)开启子进程(start)

2、获取进程编号的两种操作

  • 获取当前进程编号 os.getpid()

  • 获取当前父进程编号 os.getppid()

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

注意点:

args: 元组!!! (单个元素的元组要有 逗号)

kwargs: 字典!!!(key值要和函数中的形参完全重名)

myDance = multiprocessing.Process(target=dance, args=(5,))
mySing = multiprocessing.Process(target=sing, kwargs={"sum": 5})

4、进程间不共享全局变量

5、守护主进程

主进程默认会等待所有的子进程结束之后在结束

让子进程随着主进程的结束而结束:(两种方法)

  • 守护进程(在start之前设置)

    myFunc.daemon = True

  • 手动设置

    myFunc.terminate()

2、线程

2.1、多线程的基本使用
import threading
import time


def dance():
    for i in range(5):
        print("跳舞",i)
        time.sleep(1)

def sing():
    for i in range(5):
        print("唱歌", i)
        time.sleep(2)

if __name__ == '__main__':
    myDance = threading.Thread(target=dance)
    mySing = threading.Thread(target=sing)
    myDance.start()
    mySing.start()
2.2、多线程执行带有参数的任务
import threading
import time


def dance(count):
    for i in range(count):
        print("跳舞",i)
        time.sleep(1)

def sing(count):
    for i in range(count):
        print("唱歌", i)
        time.sleep(2)

if __name__ == '__main__':
    myDance = threading.Thread(target=dance, args=(5,))
    # kwargs:字典的key值必须和函数的形参完全重名才可以
    mySing = threading.Thread(target=sing, kwargs={"count":5})
    myDance.start()
    mySing.start()

运行后默认会创建一个进程,该进程中默认有一个主线程,主线程执行代码,再创建两个子线程

即(一个进程,一个主线程,两个子线程)

2.3、线程的注意点:
  1. 线程之间执行是无序的
  2. 主线程会等待所有的子线程执行结束在结束
  3. 线程之间共享全局变量
  4. 线程之间共享全局变量数据出现错误问题
2.3.1、线程之间执行是无序的
# 线程之间是无序执行的

import threading
import time


def Func():
    time.sleep(1)
    print(threading.current_thread())

if __name__ == '__main__':
    for i in range(5):
        myFunc = threading.Thread(target=Func)
        myFunc.start()

运行结果

<Thread(Thread-1 (Func), started 33340)>
<Thread(Thread-4 (Func), started 57196)>

<Thread(Thread-3 (Func), started 74692)>

<Thread(Thread-2 (Func), started 63812)>
<Thread(Thread-5 (Func), started 68708)>

 2.3.2、主线程会等待所有的子线程执行结束在结束

解决方法:守护主线程

# 守护线程

import threading
import time


def Func():
    for i in range(5):
        time.sleep(0.2)
        print("func")

if __name__ == '__main__':
    # 让子进程随着主进程的结束而结束
    # 守护线程
    #(1)Thread(daemon = True)
    myFunc = threading.Thread(target=Func, daemon=True)
    myFunc.start()
    time.sleep(0.5)
    print("主线程over")

运行结果

func
func
主线程over

 2.3.3、线程之间共享全局变量
# 线程之间共享全局变量
import threading
import time

globalNum = []

def myWrite():
    global globalNum
    for i in range(5):
        globalNum.append(i)
    print(globalNum)

def myRead():
    global globalNum
    print(globalNum)

if __name__ == '__main__':
    mywrite = threading.Thread(target=myWrite)
    myread = threading.Thread(target=myRead)
    mywrite.start()
    time.sleep(1)
    myread.start()

运行结果

[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]

结论:

由于线程是依附于进程的,一个进程中的所有的线程都是使用同一片内存空间

一个进程中的,线程是共享全局变量的

2.3.4、线程间共享全局变量问题
import threading

globalNum = 0

def sumNum1():
    global globalNum
    for i in range(10000000):
        globalNum += 1
    print("方法sunNum1计算的结果:", globalNum)

def sumNum2():
    global globalNum
    for i in range(10000000):
        globalNum += 1
    print("方法sunNum1计算的结果:", globalNum)

if __name__ == '__main__':
    sumnum1 = threading.Thread(target=sumNum1)
    sumnum2 = threading.Thread(target=sumNum2)
    sumnum1.start()
    sumnum2.start()

运行结果

方法sunNum1计算的结果: 19120946
方法sunNum1计算的结果: 20000000

其结果值不相等,线程间共享全局变量出现问题

解决线程间共享全局变量问题

  • 方法1:线程等待(join)
  • 方法2:互斥锁
线程等待(join):
import threading

globalNum = 0

def sumNum1():
    global globalNum
    for i in range(10000000):
        globalNum += 1
    print("方法sunNum1计算的结果:", globalNum)

def sumNum2():
    global globalNum
    for i in range(10000000):
        globalNum += 1
    print("方法sunNum1计算的结果:", globalNum)

if __name__ == '__main__':
    firstThread = threading.Thread(target=sumNum1)
    secondThread = threading.Thread(target=sumNum2)
    firstThread.start()
    # 主线程等待第一个线程执行完成以后,代码在继续执行,让其第二个线程执行
    # 线程同步:一个任务执行完以后另一个任务才能执行,同一个时刻只有一个任务执行
    firstThread.join()  # 让主线程等待firstThread执行完毕 在继续向下运行
    secondThread.start()

运行结果:

方法sunNum1计算的结果: 10000000
方法sunNum1计算的结果: 20000000

互斥锁

互斥锁:对共享数据进行锁定,保证同一时刻只能有一个线程去操作。

threading模块中定义了Lock变量,这个变量本质上是一个函数,通过调用这个函数可以获取一把互斥锁。

互斥锁的使用步骤:

# 创建锁

mutex = threading.Lock()

# 上锁

mutex.acquire()

# 释放锁

mutex.release()

注意点:

  • acquire和release方法之间的代码同一时刻只能有一个线程去操作
  • 如果在调用acquire方法的时候,其他线程已经使用了这个互斥锁,那么此时acquire方法会堵塞,直到这个互斥锁释放后才能再次上锁。
import threading

globalNum = 0
# 创建锁
mutex = threading.Lock()
def sumNum1():
    global globalNum
    # 上锁
    mutex.acquire()
    for i in range(10000000):
        globalNum += 1
    # 释放锁(如果不解锁,就会产生死锁问题)
    mutex.release()
    print("方法sunNum1计算的结果:", globalNum)

def sumNum2():
    global globalNum
    # 上锁(同一把锁,必须先解锁才可以上锁)
    mutex.acquire()
    for i in range(10000000):
        globalNum += 1
    # 释放锁
    mutex.release()
    print("方法sunNum1计算的结果:", globalNum)

if __name__ == '__main__':
    firstThread = threading.Thread(target=sumNum1)
    secondThread = threading.Thread(target=sumNum2)
    firstThread.start()
    secondThread.start()

运行结果

方法sunNum1计算的结果: 10000000
方法sunNum1计算的结果: 20000000

死锁

import threading

# 创建锁
mutex = threading.Lock()
def sumNum1():
    num = 1
    # 上锁
    mutex.acquire()
    if num == 1:
        mutex.release()  # 解除死锁
        return
    # 释放锁
    mutex.release()

def sumNum2():
    # 上锁
    mutex.acquire()
    print("sumNum2")
    # 释放锁
    mutex.release()

if __name__ == '__main__':
    firstThread = threading.Thread(target=sumNum1)
    secondThread = threading.Thread(target=sumNum2)
    firstThread.start()
    secondThread.start()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值