【Python基础】线程

文章目录

    • @[toc]
      • 线程与进程的区别与联系
      • 同步任务
        • 示例
      • 并发任务
        • 示例
      • 线程方法
        • `thread_object.start()`
        • `thread_object.join()`
        • `thread_object.setDaemon()`
        • `thread_object.current_thread()`

线程与进程的区别与联系

  • 线程是可以被计算机CPU调度的最小单元
  • 进程是计算机分配资源(CPU、内存等)的最小单元
  • 一个进程中至少有一个线程,同一个进程中的线程共享进程中的资源

同步任务

  • 我们在此之前编写的代码都是同步代码,代码从上到下按顺序执行,如果前一个任务没有完成,那么不能运行之后的任务
示例
import time


def work_1():
    print('任务1...')

    time.sleep(2)


def work_2():
    print('任务2...')

    time.sleep(2)


start = time.time()

work_1()
work_2()

end = time.time()

print(f'总共用时: {end - start} s')
任务1...
任务2...
总共用时: 4.020084857940674 s
  • 可以看到整个程序用时 4 4 4秒,work_2()需要等待work_1()运行结束后才能运行

并发任务

  • 使用线程来运行上面的代码,能够优化运行时间
示例
import time
import threading


def work_1():
    print('任务1...')

    time.sleep(2)


def work_2():
    print('任务2...')

    time.sleep(2)


# 通过 Thread 类创建线程对象, 并使用 target 绑定线程对象要运行的任务
t1 = threading.Thread(target=work_1)
t2 = threading.Thread(target=work_2)

# 运行线程
start = time.time()

t1.start()
t2.start()

t1.join()
t2.join()

end = time.time()

print(f'总共用时: {end - start} s')
任务1...
任务2...
总共用时: 2.0165793895721436 s
  • 可以看到整个程序用时 2 2 2秒,work_1()work_2并发运行

  • 下面的示例可以看到CPU调度线程时的“随机性”

import time
import threading


def work_1():
    for i in range(5):
        print('任务1...')

        time.sleep(2)


def work_2():
    for i in range(5):
        print('任务2...')

        time.sleep(2)


t1 = threading.Thread(target=work_1)
t2 = threading.Thread(target=work_2)

t1.start()
t2.start()
任务1...
任务2...
任务2...任务1...

任务1...任务2...

任务2...
任务1...
任务2...
任务1...
  • 可以看到任务 1 1 1和任务 2 2 2的调度顺序是我们无法确定的,是由CPU的调度算法决定的

线程方法

  • 在学习线程方法之前,我们需要知道Python程序是如何被运行的
    • 一个Python文件被解释器运行时会在操作系统中创建一个进程
    • 然后该进程会创建一个线程来运行文件中的代码,这个程序最初创建的线程称为主线程
    • 当主线程执行到t = threading.Thread()时会创建一个新的线程,称为子线程
    • 当前进程中的主线程与子线程由CPU进行调度,并发地运行,具体调度哪个线程由操作系统的调度算法决定
    • 子线程在运行时,主线程不会等待子线程,而是继续向下执行,直到执行到文件末尾没有代码时,主线程会等待子线程运行结束后再退出
thread_object.start()
  • t = threading.Thread()只是创建了一个线程,并不会执行线程代码

  • t.start()使线程t达到就绪态,等待CPU进行调度,具体何时调度由CPU决定

  • 以上面的并发任务的代码为例,先注释掉t1.start()t2.start()

import time
import threading


def work_1():
    print('任务1...')

    time.sleep(2)


def work_2():
    print('任务2...')

    time.sleep(2)


# 通过 Thread 类创建线程对象, 并使用 target 绑定线程对象要运行的任务
t1 = threading.Thread(target=work_1)
t2 = threading.Thread(target=work_2)

# 运行线程
start = time.time()

t1.start()
t2.start()

# t1.join()
# t2.join()

end = time.time()

print(f'总共用时: {end - start} s')
任务1...
任务2...
总共用时: 0.0009987354278564453 s
  • 可以看到主线程没有等待子线程,而是继续向下执行
  • 当执行到end = time.time()时,此时end记录的时间是主线程运行到这行代码的时间
  • 之后运行print(f'总共用时: {end - start} s'),输出时间0.0009987354278564453 s,此时执行到了文件末尾没有其他代码,主线程会等待子线程运行结束后再退出
  • 为了能正确记录线程运行的时间,我们需要让主线程等待子线程
thread_object.join()
  • t.join()使主线程等待子线程,子线程任务执行结束后主线程再继续向下执行
  • 仍然以上面的并发任务的代码为例,取消注释t1.start()t2.start()
import time
import threading


def work_1():
    print('任务1...')

    time.sleep(2)


def work_2():
    print('任务2...')

    time.sleep(2)


# 通过 Thread 类创建线程对象, 并使用 target 绑定线程对象要运行的任务
t1 = threading.Thread(target=work_1)
t2 = threading.Thread(target=work_2)

# 运行线程
start = time.time()

t1.start()
t2.start()

t1.join()
t2.join()

end = time.time()

print(f'总共用时: {end - start} s')
任务1...
任务2...
总共用时: 2.008962392807007 s
  • 可以看到主线程等待子线程运行结束后才继续向下执行,正确记录了子线程运行的时间
thread_object.setDaemon()
  • 设置守护线程,需要在线程启动之前进行设置
  • 如果一个线程是守护线程,那么主线程运行结束后不论子线程任务是否结束都会自动退出
  • 没有设置守护线程的情况
import time
import threading


def work():
    for i in range(5):
        print(i)

        time.sleep(1)


t = threading.Thread(target=work)
# t.setDaemon(True)

t.start()

print('主线程即将退出...')
0
主线程即将退出...
1
2
3
4
  • 设置守护线程的情况
import time
import threading


def work():
    for i in range(5):
        print(i)

        time.sleep(1)


t = threading.Thread(target=work)
t.setDaemon(True)

t.start()

print('主线程即将退出...')
0
主线程即将退出...
  • 可以看到并没有继续输出 1 1 1 2 2 2 3 3 3 4 4 4,主线程就退出了
thread_object.current_thread()
  • 获取当前线程对象的引用
  • 可以用来获取线程名称
import threading


def work():
    name = threading.current_thread().name  # getName()

    print(name)


for i in range(5):
    t = threading.Thread(target=work)
    t.name = f'线程-{i}'  # setName(f'线程-{i}')

    t.start()

你可能感兴趣的:(Python基础,Python,线程)