Python | 进程、线程是什么?它们有何区别,如何运用,进程与线程介绍

进程与线程

进程与线程的概念理解

对于进程与线程,我们可以简单类比为:
一个工厂,至少要有一个车间,一个车间至少有一个工人,工人在工作->工厂运行
一个程序,至少要有一个进程,一个进程至少有一个线程,线程在工作->程序运行

提高效率方式:
增加线程:
一个工厂,一个车间,一个车间两个工人,工人多了->工厂效率提高
一个程序,一个进程,一个进程两个线程,线程多了->程序效率提高

增加进程:
一个工厂,两个车间,每一个车间一个工人,车间多了->工厂效率提高
一个程序,两个进程,每一个进程一个线程,进程多了->程序效率提高

默认情况下,我们开发的程序都是通过串行的形式运行的,排队逐一执行,前面未运行完成,后面的无法直接运行,如:

import time

number1 = 100000000
number2 = 200000000

time1 = time.time()

result = 0
for i in range(number1):
    result += 1
print(result)

result = 0
for i in range(number2):
    result += 1
print(result)

time2 = time.time()
print(f'耗时{time2-time1}秒')

#运行结果:
#100000000
#200000000
#耗时20.385773420333862秒

所谓串行,并行,并发

串行:任务按严格顺序依次执行,前一个任务完成后才能开始下一个任务。

并行:多个任务物理上同时执行,需依赖多核CPU或多处理器系统

并发:多个任务逻辑上同时执行,通过时间片轮转快速切换,宏观看似并行,微观仍是串行 

Python | 进程、线程是什么?它们有何区别,如何运用,进程与线程介绍_第1张图片

串行程序变为并发程序

通过进程和线程,我们可以将串行程序变为并发程序

增加线程:

一个工厂,一个车间,一个车间两个工人,工人多了->工厂效率提高
一个程序,一个进程,一个进程两个线程,线程多了->程序效率提高

import time
import threading

def task(number):
    result = 0
    for i in range(number):
        result += 1
    print(result)

time1 = time.time()
#创建线程
numbers = [100000000,200000000]
threads = [] #记录线程
for num in numbers:
    t = threading.Thread(target=task, args=(num,)) #创建多线程,target为运行的进程函数,args为传参(单个参数记得加逗号)
    t.start() #启动线程
    threads.append(t)

for t in threads:
    t.join() #阻塞主线程,强制主线程等待所有子线程完成

time2 = time.time()
print(f'多线程方式,耗时{time2-time1}秒')

运行结果:
100000000
200000000
多线程方式,耗时8.283299684524536秒 

增加进程:

一个工厂,两个车间,每一个车间一个工人,车间多了->工厂效率提高
一个程序,两个进程,每一个进程一个线程,进程多了->程序效率提高

import time
import multiprocessing

def task(number):
    result = 0
    for i in range(number):
        result += 1
    print(result)

if __name__ == '__main__':
    time1 = time.time()
    #创建进程
    numbers = [100000000,200000000]
    process = [] #记录进程
    for num in numbers:
        p = multiprocessing.Process(target=task, args=(num,)) #创建多进程,target为运行的进程函数,args为传参(单个参数记得加逗号)
        p.start() #启动进程
        process.append(p)

    for p in process:
        p.join() #阻塞主线程,强制主线程等待所有子进程完成
    
    time2 = time.time()
    print(f'多进程方式,耗时{time2-time1}秒')

运行结果:

100000000

200000000

多进程方式,耗时6.042091131210327秒

GIL锁

GIL,全局解释锁,是CPython解释器特有的,让一个进程中的同一时刻只能有一个线程可以被CPU调度

所以如果程序需利用计算机多核优势,让CPU同时处理一些任务,适合用多进程开发(资源开销大)

若程序不利用计算机多核优势,适合多线程开发

那么我们什么时候用多进程,什么时候用多线程呢?

IO密集型:较少使用计算机CPU资源用多线程,例如:文件读写,网络数据传输
计算密集型:以计算为主用多进程,例如大量的数据计算,占用资源比较多的

IO密集型 

下载百度小说中的《西游记》

import requests
import threading
import json


def down(title,cid):
    data = {
        "book_id": "4306063500",
        "cid": f"4306063500|{cid}",
        "need_bookinfo": 1
    }
    data_str = json.dumps(data)
    down_url = f'https://dushu.baidu.com/api/pc/getChapterContent?data={data_str}'
    down_resp = requests.get(down_url)
    dics = down_resp.json()
    dic = dics['data']['novel']['content']
    f = open(f"西游记/{title}.txt", mode="w", encoding="utf-8")
    f.write(dic)
    print(title,cid)

url = 'https://dushu.baidu.com/api/pc/getCatalog?data={"book_id":"4306063500"}'
resp = requests.get(url)
data = resp.json()
data = data['data']['novel']['items']
threads = []
for i in data:
    title = i['title']
    cid = i['cid']
    t = threading.Thread(target=down, args=(title, cid))
    t.start()
    threads.append(t)

for t in threads:
    t.join()

print('over')

计算密集型:

import time
import multiprocessing

def task(number,queue):
    result = 0
    for i in range(number):
        result += 1
    queue.put(result)

if __name__ == '__main__':
    time1 = time.time()
    queue = multiprocessing.Queue()
    #创建进程
    numbers = [100000000,100000,7,200000000,100000,10,300000000,500,10,20,7,4]
    process = [] #记录进程
    for num in numbers:
        p = multiprocessing.Process(target=task, args=(num,queue))
        p.start() #启动线程
        process.append(p)

    for p in process:
        p.join() #阻塞主线程,强制主线程等待所有子进程完成

    numsum = 0
    while not queue.empty():
        numsum += queue.get()
    print(f'数组和为:{numsum}')
    
    time2 = time.time()
    print(f'耗时{time2-time1}')

多线程开发

基础用法

t.start() 用于启动一个新线程,使其进入就绪状态,等待系统调度执行。
t.join() 使主线程等待目标子线程t执行完毕后再继续执行。
t.daemon 用于设置线程的守护属性(必须放在start之前),决定线程是否随主线程退出而强制终止。 例:t.daemon = False (默认值,不强制终止)
t.name 为子线程设置名字(放在start之前)。 例:t.name = 't1'

import time
import threading

def task(number):
    print("当前线程的名称:",threading.current_thread().name)
    result = 0
    for i in range(number):
        result += 1
    print(result)

time1 = time.time()
#创建线程
numbers = [100000000,200000000]
threads = [] #记录线程
for num in numbers:
    t = threading.Thread(target=task, args=(num,)) #创建多线程,target为运行的进程函数,args为传参(单个参数记得加逗号)
    t.name = 't'+str(num) #线程命名
    t.daemon = False #不随主流程终止
    t.start() #启动线程
    threads.append(t)

for t in threads:
    t.join() #阻塞主线程,强制主线程等待所有子线程完成

time2 = time.time()
print(f'多线程方式,耗时{time2-time1}秒')

自定义线程类

通过自定义线程类,可直接将线程需要做的事写到run方法中

import time
import threading

class MyThread(threading.Thread):
    def run(self):
        print('执行次线程',self._args)
        num, = self._args
        nsum = 0
        for i in range(num):
            nsum += 1
        print(f'累加和为{nsum}')

t = MyThread(args=(100,))
t.start()

线程安全

一个进程可以有多个线程,多个线程共享一个资源
多个线程同时去操作一个'变量'可能会存在数据混乱的情况
所以为了保证线程安全,可使用线程锁
创建锁:lock = threading.RLock()
申请锁:lock.acquire()
释放锁:lock.release()

import threading
import time

num = 500000000
result = 0
lock = threading.RLock() #创建锁
time1 = time.time()

def Add(number):
    lock.acquire() #申请锁
    global result
    for i in range(number):
        result += 1
    lock.release() #释放锁
        
def Sub(number):
    lock.acquire() #申请锁
    global result
    for i in range(number):
        result -= 1
    lock.release() #释放锁

t1 = threading.Thread(target=Add, args=(num,))
t2 = threading.Thread(target=Sub, args=(num,))
t1.start()
t2.start()

t1.join()
t2.join()

time2 = time.time()
print(result)
print(f'耗时{time2-time1}')

线程池

程序中,线程并不是越多越好的,开的线程多了可能会导致系统的性能更低,所以使用线程池是很有必要的
pool = ThreadPoolExecutor(10) 创建线程池,最多维护10个线程
pool.submit(fun, arg) 提交一个线程至线程池,若线程池中有空余线程即运行该线程,反之则等待线程池空缺
pool.shutdown(True) 等待线程池中的任务全部执行完毕

import time
from concurrent.futures import ThreadPoolExecutor

def task(number):
    result = 0
    for i in range(number):
        result += 1
    print(result)
    
pool = ThreadPoolExecutor(10)


for num in range(100):
    pool.submit(task, num)
    
pool.shutdown(True) #等待线程池中的全部任务执行完毕
print('over')

利用线程池下载百度小说中的《西游记》

import requests
import json
from concurrent.futures import ThreadPoolExecutor


def down(title,cid):
    data = {
        "book_id": "4306063500",
        "cid": f"4306063500|{cid}",
        "need_bookinfo": 1
    }
    data_str = json.dumps(data)
    down_url = f'https://dushu.baidu.com/api/pc/getChapterContent?data={data_str}'
    down_resp = requests.get(down_url)
    dics = down_resp.json()
    dic = dics['data']['novel']['content']
    f = open(f"西游记/{title}.txt", mode="w", encoding="utf-8")
    f.write(dic)
    return(f"{title}下载完成,路径为'西游记/{title}'.txt")

url = 'https://dushu.baidu.com/api/pc/getCatalog?data={"book_id":"4306063500"}'
resp = requests.get(url)
data = resp.json()
data = data['data']['novel']['items']

pool = ThreadPoolExecutor(10)

podata_list = []
for i in data:
    title = i['title']
    cid = i['cid']
    podata = pool.submit(down, title, cid)
    podata_list.append(podata)

pool.shutdown(True) #等待线程池中的任务执行完毕
print('over')

for dat in podata_list:
    print(dat.result()) #获取每个线程的返回值

多进程开发

基础用法

一个进程可以有多个线程,线程之间共享一个进程资源,进程与进程之间是相互隔离的,资源并不共享

p.start() 用于启动一个新线程,使其进入就绪状态,等待系统调度执行。
p.join() 使主线程等待目标子线程t执行完毕后再继续执行。
p.daemon 用于设置线程的守护属性(必须放在start之前),决定线程是否随主线程退出而强制终止。 例:p.daemon = False (默认值,不强制终止)
p.name 为子线程设置名字(放在start之前)。 例:p.name = 'p1'

import time
import multiprocessing

def task(number):
    print("当前进程名称:",multiprocessing.current_process().name)
    result = 0
    for i in range(number):
        result += 1
    print(result)

if __name__ == '__main__':
    time1 = time.time()
    #创建进程
    numbers = [100000000,200000000]
    process = [] #记录进程
    for num in numbers:
        p = multiprocessing.Process(target=task, args=(num,)) #创建多进程,target为运行的进程函数,args为传参(单个参数记得加逗号)
        p.name = 'p'+str(num) #进程命名
        p.daemon = False #不随主流程关闭子进程
        p.start() #启动进程
        process.append(p)

    for p in process:
        p.join() #阻塞主线程,强制主线程等待所有子进程完成
    
    time2 = time.time()
    print(f'多进程方式,耗时{time2-time1}秒')

获取计算机的 CPU 数量

import multiprocessing
count = multiprocessing.cpu_count()
print(count)

自定义进程类

通过自定义进程类,可直接将进程需要做的事写到run方法中

import time
import multiprocessing

class MyThread(multiprocessing.Process):
    def __init__(self, num):  # 添加构造函数接收参数
        super().__init__()
        self.num = num

    def run(self):
        print('执行子进程')
        nsum = 0
        for i in range(self.num):
            nsum += 1
        print(f'累加和为{nsum}')

if __name__ == '__main__':
    t = MyThread(100)  # 直接传递参数给构造函数
    t.start()
    t.join()  # 建议添加join等待子进程结束

进程安全

当多个进程同时访问和操作共享资源时,执行顺序的不确定性可能导致意外结果
为防止该问题的出现,可通过进程锁来避免
创建锁:lock = multiprocessing.RLock()
申请锁:lock.acquire()
释放锁:lock.release()

import multiprocessing
import time

def Add(number, result, lock):
    lock.acquire()  # 申请锁
    for i in range(number):
        result.value += 1
    lock.release()  # 释放锁
        
def Sub(number, result, lock):
    lock.acquire()  # 申请锁
    for i in range(number):
        result.value -= 1
    lock.release()  # 释放锁

if __name__ == '__main__':
    num = 500000000
    # 使用Value来共享变量
    result = multiprocessing.Value('i', 0)
    # 创建锁
    lock = multiprocessing.RLock()
    time1 = time.time()

    p1 = multiprocessing.Process(target=Add, args=(num, result, lock))
    p2 = multiprocessing.Process(target=Sub, args=(num, result, lock))
    p1.start()
    p2.start()

    p1.join()
    p2.join()

    time2 = time.time()
    print(result.value)
    print(f'耗时{time2-time1}')

进程池

程序中,进程并不是越多越好的,开的进程多了可能会导致系统的性能更低,所以使用进程池是很有必要的
pool = ProcessPoolExecutor(10) 创建进程池,最多维护10个进程
pool.submit(fun, arg) 提交一个进程至进程池,若进程池中有空余进程即运行该进程,反之则等待进程池空缺
pool.shutdown(True) 等待进程池中的任务全部执行完毕

from concurrent.futures import ProcessPoolExecutor

def task(name):
    print(f"Process {name} is running")

if __name__ == '__main__':
    # 创建最大容量为10的进程池
    pool = ProcessPoolExecutor(10)
    
    # 提交任务到进程池
    for i in range(15):
        pool.submit(task, i)
    
    # 等待所有任务完成
    pool.shutdown(True)
    print("All processes completed")

你可能感兴趣的:(Python,python,java,linux)