python -- 认识多线程threading模块

一、了解多线程

  • 线程可以理解成轻量级的进程,是操作系统能够进行运算调度的最小单位,它包含在进程中,是进程的实际运作单位
  • 线程自身并不拥有系统资源,只拥有一些运行中必不可少的资源,但它可以与同一个进程的其他线程共享进程所拥有的的全部资源。
  • 一个线程可以创建和撤销另一个线程,同一个进程中的多个线程可以并发执行

线程有三种基本状态:
1、就绪状态:指线程具备运行的所有条件,逻辑上可以运行,在等待处理机会
2、运行状态:指线程占用处理机会,并正在运行
3、阻塞状态:值线程在等待一个事件(如某个信号量),逻辑上不可执行
在这里插入图片描述

由于GIL(全局解释器锁)的存在,python的多线程只能调用一个CPU,因此在当今多核的情况下,成为了一个缺陷。因此在执行计算密集型任务时,适合使用多进程;当执行I/O密集型任务时,适合使用多线程

1.计算密集型的多进程和多线程比较

1.1、多进程

from multiprocessing import Process
import time
import os

# 计算密集型任务
def work():
    res = 0
    for i in range(100000000):
        res *= i

if __name__ == '__main__':
    l = []
    print(f"本机为{os.cpu_count()}核cpu机器")        # 本机为12核cpu机器
    start = time.time()
    for i in range(4):
        p = Process(target=work)
        l.append(p)
        p.start()

    for i in l:
        i.join()

    end = time.time()

    print("计算密集型任务,多进程耗时:%s" % (end - start))

执行结果:耗时5.4秒

本机为12核cpu机器
计算密集型任务,多进程耗时:5.387600898742676

1.2、执行多线程

from threading import Thread
import time
import os

# 计算密集型任务
def work():
    res = 0
    for i in range(100000000):
        res *= i

if __name__ == '__main__':
    l = []
    print(f"本机为{os.cpu_count()}核cpu机器")        # 本机为12核cpu机器
    start = time.time()
    for i in range(4):
        p = Thread(target=work)
        l.append(p)
        p.start()

    for i in l:
        i.join()

    end = time.time()

    print("计算密集型任务,多进程耗时:%s" % (end - start))

执行结果:执行时间为16.3秒,在执行计算密集型任务,比多线程慢了近近3倍。

本机为12核cpu机器
计算密集型任务,多进程耗时:16.26915717124939

2.I/O密集型的多进程和多线程比较

2.1、执行多进程

from multiprocessing import Process
import time
import os

# I/O密集型任务
def work():
    time.sleep(2)
    print("===>", file=open("tmp.txt", "w"))

if __name__ == '__main__':
    l = []
    print(f"本机为{os.cpu_count()}核cpu机器")        # 本机为12核cpu机器
    start = time.time()
    for i in range(4):
        p = Process(target=work)
        l.append(p)
        p.start()

    for i in l:
        i.join()

    end = time.time()

    print("计算密集型任务,多进程耗时:%s" % (end - start))

执行结果:执行耗时2.1秒

本机为12核cpu机器
计算密集型任务,多进程耗时:2.137941837310791

2.3、执行多线程

from threading import Thread
import time
import os

# I/O密集型任务
def work():
    time.sleep(2)
    print("===>", file=open("tmp.txt", "w"))     # 将 ===》 写入到 tmp.txt文件

if __name__ == '__main__':
    l = []
    print(f"本机为{os.cpu_count()}核cpu机器")        # 本机为12核cpu机器
    start = time.time()
    for i in range(4):
        p = Thread(target=work)
        l.append(p)
        p.start()

    for i in l:
        i.join()

    end = time.time()

    print("计算密集型任务,多进程耗时:%s" % (end - start))

执行结果:执行I/O密集型任务,比多进程快了一些

本机为12核cpu机器
计算密集型任务,多进程耗时:2.0026445388793945

二、多线程threading模块

  • python提供的多线程模块有两个:threading 和 _thread,threading是对_thread的封装,功能更强大

1、示例1:通过函数的方式执行多线程:

import threading
import time

def task_thread(counter):
    print(f'线程名称:{threading.current_thread().name} 参数:{counter} 开始时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')
    num = counter
    while num:
        time.sleep(2)
        num -= 1
    print(f'线程名称:{threading.current_thread().name} 参数:{counter} 结束时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')


if __name__ == '__main__':
    print(f"主线程开始时间:{time.strftime('%Y-%m-%d %H:%M:%S')}")

    # 实例化3个线程,传入不同的参数
    t1 = threading.Thread(target=task_thread, args=(3,))
    t2 = threading.Thread(target=task_thread, args=(2,))
    t3 = threading.Thread(target=task_thread, args=(1,))
    t1.start()
    t2.start()
    t3.start()
    t1.join()
    t2.join()
    t3.join()
    print(f"主线程结束时间:{time.strftime('%Y-%m-%d %H:%M:%S')}")

执行结果:

主线程开始时间:2021-05-19 23:57:46
线程名称:Thread-1 参数:3 开始时间:2021-05-19 23:57:46
线程名称:Thread-2 参数:2 开始时间:2021-05-19 23:57:46
线程名称:Thread-3 参数:1 开始时间:2021-05-19 23:57:46

线程名称:Thread-3 参数:1 结束时间:2021-05-19 23:57:48

线程名称:Thread-2 参数:2 结束时间:2021-05-19 23:57:50

线程名称:Thread-1 参数:3 结束时间:2021-05-19 23:57:52
主线程结束时间:2021-05-19 23:57:52

Process finished with exit code 0

2.示例2:通过类的方式执行多线程

  • 继承Thread类实现多线程
import threading
import time

class Mythread(threading.Thread):
    def __init__(self, counter):
        super().__init__()            # 调用父类的__init__()方法
        self.counter = counter

    def run(self):
        print(f'线程名称:{threading.current_thread().name} 参数:{self.counter} 开始时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')
        num = self.counter
        while num:
            time.sleep(2)
            num -= 1
        print(f'线程名称:{threading.current_thread().name} 参数:{self.counter} 结束时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')


if __name__ == '__main__':
    print(f"主线程开始时间:{time.strftime('%Y-%m-%d %H:%M:%S')}")

    # 实例化3个线程,传入不同的参数
    t1 = Mythread(3)
    t2 = Mythread(2)
    t3 = Mythread(1)
    t1.start()
    t2.start()
    t3.start()
    t1.join()
    t2.join()
    t3.join()
    print(f"主线程结束时间:{time.strftime('%Y-%m-%d %H:%M:%S')}")

执行结果

主线程开始时间:2021-05-20 00:05:13
线程名称:Thread-1 参数:3 开始时间:2021-05-20 00:05:13
线程名称:Thread-2 参数:2 开始时间:2021-05-20 00:05:13
线程名称:Thread-3 参数:1 开始时间:2021-05-20 00:05:13

线程名称:Thread-3 参数:1 结束时间:2021-05-20 00:05:15

线程名称:Thread-2 参数:2 结束时间:2021-05-20 00:05:17

线程名称:Thread-1 参数:3 结束时间:2021-05-20 00:05:19
主线程结束时间:2021-05-20 00:05:19

Process finished with exit code 0

3.示例3:多线程的类方法中引入类外的函数

  • 通过self.target来接收外部函数,self.args来接收传入外部函数内的参数
  • 通过self.target(*self.args) 实现调用外部函数
  • self.target 其实就是外部函数的内存地址
import threading
import time

# 定义外部函数
def out_func(counter):
    print(f'线程名称:{threading.current_thread().name} 参数:{counter} 开始时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')
    num = counter
    while num:
        time.sleep(2)
        num -= 1
    print(f'线程名称:{threading.current_thread().name} 参数:{counter} 结束时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')


class Mythread(threading.Thread):
    def __init__(self, target, args):
        super().__init__()
        self.target = target
        self.args = args

    def run(self):
        self.target(*self.args)   # *self.args,打散args参数

if __name__ == '__main__':
    print(f"主线程开始时间:{time.strftime('%Y-%m-%d %H:%M:%S')}")

    # 实例化3个线程,传入不同的参数
    t1 = Mythread(target=out_func, args=(3,))
    t2 = Mythread(target=out_func, args=(2,))
    t3 = Mythread(target=out_func, args=(1,))
    t1.start()
    t2.start()
    t3.start()
    t1.join()
    t2.join()
    t3.join()
    print(f"主线程结束时间:{time.strftime('%Y-%m-%d %H:%M:%S')}")

执行结果:

主线程开始时间:2021-05-20 00:13:06
线程名称:Thread-1 参数:3 开始时间:2021-05-20 00:13:06
线程名称:Thread-2 参数:2 开始时间:2021-05-20 00:13:06
线程名称:Thread-3 参数:1 开始时间:2021-05-20 00:13:06

线程名称:Thread-3 参数:1 结束时间:2021-05-20 00:13:08

线程名称:Thread-2 参数:2 结束时间:2021-05-20 00:13:10

线程名称:Thread-1 参数:3 结束时间:2021-05-20 00:13:12
主线程结束时间:2021-05-20 00:13:12

Process finished with exit code 0
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值