python笔记:线程

以下是自己学习后总结的知识点,如有错误请大家指出错误,我来修改。

线程基础知识

1、线程是CPU能够进行运算调度的最小单位。

2、进程是计算机分配资源的的最小单元,进程可以为线程提供运行资源。

3、一个进程中可以有多个线程,同一个进程中的线程可以共享当前进程的资源。此外线程是进程中的一个实体。

4、线程的基本状态为新建、就绪、运行、阻塞和结束。

使用线程

我们以前刚刚开始接触的python代码都是同步单线程代码,都是依次运行的。

1、比如写了两个任务函数。使用线程实现。

import threading
import time


def task_1():
    print('前_任务1')
    time.sleep(6)
    print('后_任务1')

def task_2():
    print('前_任务2')
    time.sleep(4)
    print('后_任务2')

if __name__ == '__main__':
    t1 = threading.Thread(target=task_1)
    t2 = threading.Thread(target=task_2)

    t1.start()
    t2.start()

    print("执行完毕")

运行结果为: 

前_任务1
前_任务2
执行完毕
后_任务2
后_任务1

可以看出程序运行并不等待所以任务完成才执行最后的打印,而是打印两个任务程序的前_任务后直接执行打印执行完毕,再继续等待。 

t1 = threading.Thread(target=task_1)  # 创建子线程

t1.start()  # 当前线程准备就绪,等待调度,具体调度时间由cpu决定。

上述代码中我们应该要知道:

1、一个py文件被解释器执行时会在操作系统中创建进程。

2、在进程中创建线程来执行当前文件中的代码,我们把最初创建的线程称之为主线程。

3、当主线程执行到Thread代码时会创建一个新的线程,我们一般称之为子线程。

4、当前代码中的主线程与子线程交替执行。

5、子线程被执行时主线程不会等待,继续往下执行到没有代码时等待子线程执行完毕再退出。

但是在一下程序中我们遇到了一个问题像这样:

import threading


num = 0

def add():
    global num
    for i in range(100000):
        num += i

t = threading.Thread(target=add)
t.start()
print(num)




# 连续五次都不一样,这是为什么呢?

 原因是主线程并不会等待子线程执行完成后才结束,而是主线程执行到打印函数后直接结束程序。我们怎么解决这个问题?

接下来我们要使用join()来解决这个问题。我们先看程序:

import threading


num = 0

def add():
    global num
    for i in range(100000):
        num += i

t = threading.Thread(target=add)
t.start()
t.join()
print(num)

 连续运行五次结果都为:4999950000。

 join()的作用是堵塞主线程,当子线程执行完毕后才解开堵塞,让主线程继续往下执行。

 守护线程
# t.deamon=True 的作用是将线程 t 设置为守护线程。
# 例如:


import time
import threading


def work():
    for i in range(5):
        print(i)
        time.sleep(1)


t = threading.Thread(target=work)
t.daemon=False
t.start()
print('主线程即将退出...')



 

t.deamon = False  代表着该线程为非守护线程,作用为主线程结束时,如果还有非守护线程还在运行,则等待这些非守护线程执行完毕。

t.deamon = True  代表着该线程为守护线程,当主线程结束时守护线程自动终止,不会阻止程序的退出。
 

 线程池
import time
from concurrent.futures import ThreadPoolExecutor  # 线程池库

# 创建线程池对象
# max_workers:最大线程数
executor = ThreadPoolExecutor(max_workers=2)

def task(tt):
    print('---开始执行任务---')
    time.sleep(tt)
    print('---任务执行完毕---')


if __name__ == '__main__':
    # 提交任务
    # 通过submit提交需要执行的函数到线程池中,并且submit是立即返回对象不会堵塞
    t1 = executor.submit(task, 3)
    t2 = executor.submit(task, 2)
    t3 = executor.submit(task, 4)

    # done方法用于判定某个任务是否完成
    print('t1完成情况:', t1.done())
    print('t2完成情况:', t2.done())
    print('t3完成情况:', t3.done())

    # 可以使用cancel取消任务 但是运行中的任务无法取消,可以将线程数量修改成1
    # print('t2任务取消:', t2.cancel())
    # print('t2任务取消:', t2.cancel())
    # print('t2任务取消:', t2.cancel())

    # result方法可以获取任务的返回值 当前获取为阻塞
    print('t1返回结果:', t1.result())
    print('t2返回结果:', t2.result())
    print('t3返回结果:', t3.result())

使用场景

 我们使用线程时需要在什么场景使用?

           1、属于IO密集型任务那么优先使用线程方式。

           2、属于计算密集型任务那么优先使用进程方式

那么什么是IO密集型任务什么是计算密集型任务?

         IO密集型任务:指在程序运行中花了大量时间在输入输出操作上,这类任务的特点是频繁的与外部进行资源交换,如读写磁盘文件,网络请求等。

        计算密集型任务:指主要消耗CPU资源的任务,花大量时间在数据计算和数据处理上。对CPU的计算能力要求较高如:视频或音频解码,科学计算等。

python全局解释器锁(GIL锁)

主要功能:是让一个进程在同一时刻只允许一个线程进行工作

例如在某一时刻同一个线程中,只有一个线程在执行,其他线程在等待cpu调度。这种情况无法发挥出多核cpu的优势。如果想绕开GIL锁,只能使用多进程的方式,创建多个进程是极其浪费计算机资源的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值