04.python-多线程

介绍

线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程中,是进程中的实际运作单位。一个进程中可以并发多个线程,每条线程并行执行不同的任务。同一进程中的多个线程共享进程中的全部系统资源。

python中的多线程因为全局解释器锁GIL的原因限制同一时刻只能由一个线程运行,无法发挥多核CPU的优势【当然这是在默认解释器CPython中的缺陷,在JPython中没有GIL的问题,以下基于CPython解释器】。所以:GIL并不是python的特性,python也不依赖于GIL。说了那么多,那么python中的多线程是不是没用了?

答案当然不是。这取决于线程执行的场景是什么?是做计算(计算密集型)还是输入输出(I/O密集型),针对不同的场景使用不同的方法。多核心CPU可以有多个核心并行完成计算,所以多核提升的是计算性能,但每个CPU一旦遇到I/O阻塞,仍然需要等待,所以多核对I/O密集型任务没有太大的提升,多线程适合处理I/0密集型程序,如文件读写,web请求,数据库请求等。

使用

以下演示使用多线程对一个变量值进行修改,在循环的次数不多时修改后变量的值是符合预期的,当增加循环次数后,变量最终的值并不符合预期。由此可见:线程之间资源是存在竞争的,修改同一份资源必须加互斥锁,同时需要避免死锁。

# coding=utf-8
import threading

# 定义一个字段。多线程执行+1操作
balance = 0

def worker1():
    global balance
    for i in range(1000):
        balance += 1
    print('线程1执行完成,balance='+str(balance))

def worker2():
    global balance
    for i in range(1000):
        balance += 1
    print('线程2执行完成,balance='+str(balance))

def main():
    # 构造线程对象
    t1 = threading.Thread(target=worker1)
    t2 = threading.Thread(target=worker2)
    # 开始执行
    t1.start()
    t2.start()

    """
    循环次数为1000时,程序输出:
        线程1执行完成,balance=1000
        线程2执行完成,balance=2000
    循环次数为1000000时,程序输出:
        线程1执行完成,balance=1180919
        线程2执行完成,balance=1179703
    """    

if __name__ == '__main__':
    main()

要想解决以下的问题,需要使用线程的锁对象,只需要对worker1和woker2方法进行修改。

# 创建一个互斥锁,默认是未锁定状态
mutex = threading.Lock()

def worker1():
    global balance
    for i in range(1000000):
        mutex.acquire()
        balance += 1
        mutex.release()
    print('线程1执行完成,balance=' + str(balance))

def worker2():
    global balance
    for i in range(1000000):
        mutex.acquire()
        balance += 1
        mutex.release()
    print('线程2执行完成,balance=' + str(balance))

"""
加了互斥锁之后的输出:
    线程1执行完成,balance=1941343
    线程2执行完成,balance=2000000
"""

特点:

  • 线程执行的顺序是不确定的
  • 主线程【进程】会等待所有子线程结束后才会退出,主线程【进程】结束么子线程必然结束
  • 线程间共享资源
  • 修改资源必要时需要加锁,同时避免死锁
  • 占用的资源比进程少
  • 线程并不是越多越快
  • 由于GIL的原因,多线程并不是真正的并发,只是交替执行
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!