一篇文章学习Python中的多线程

一、什么是多线程Threading

二、添加线程Thread

import threading                   # 导入模块

print(threading.active_count())    # 获取激活的线程数
print(threading.enumerate())       # 查看所有线程信息
print(threading.current_thread())  # 查看正在运行的线程

def thread_job():
    print("This is thread of %s",threading.current_thread())

if __name__ == '__main__':
    print("This is thread of %s",threading.current_thread())
    thread = threading.Thread(target=thread_job,)   # 定义线程:注意target只写函数名,不能带括号
    thread.start()                                  # 让线程开始工作

结果:

1
[<_MainThread(MainThread, started 4661450176)>]
<_MainThread(MainThread, started 4661450176)>
This is thread of %s <_MainThread(MainThread, started 4661450176)>
This is thread of %s <Thread(Thread-1, started 123145514528768)>

三、join功能

1. 不使用join()的结果:主线程Main thread 和子线程 Thread1 同时进行

import threading 
import time                  

def thread_job():
    print("Thread1 start")
    time.sleep(1)                # 模拟任务消耗时间
    print("Thread1 finish")

if __name__ == '__main__':
    thread = threading.Thread(target=thread_job,name="Thread1")          
    thread.start() 
    print("All finish")

预期结果:

Thread1 start
Thread1 finish
All finish

实际结果:

Thread1 start
All finish
Thread1 finish

2. 使用join()的结果:在子线程 Thread1 完成之后再执行主线程Main thread

if __name__ == '__main__':
    thread = threading.Thread(target=thread_job,name="Thread1")          
    thread.start() 
    thread.join()
    print("All finish")

结果:

Thread1 start
Thread1 finish
All finish

3. 如果是多个线程,为了规避不必要的麻烦,推荐1221的V型排布

thread_1.start() 
thread_2.start() 
thread_2.join()
thread_1.join()
print("All finish")

四、存储进程结果Queue

import threading 
import time  
from queue import Queue                

def thread_job(l,q):          # l:列表,q:队列,功能:计算列表每个元素的平凡,结果保存在队列中
    for i in range(len(l)):
        l[i] = l[i]**2
    q.put(l)                  # 多线程调用的函数不能用return返回值,故用队列保存结果

if __name__ == '__main__':
    q = Queue()
    data = [[1,2,3],[4,5,6],[7,8,9]]
    threads = []
    for i in range(3):        # 定义三个线程
        t = threading.Thread(target=thread_job,args=(data[i],q))
        t.start()             # 开启线程
        threads.append(t)     # 把每个线程加入到线程列表中

    for thread in threads:    # 将3个线程join到主线程中
        thread.join()

    for _ in range(3):
        print(q.get())        # 使用q.get()按顺序从q中取出结果

结果:

[1, 4, 9]
[16, 25, 36]
[49, 64, 81]

五、GIL不一定有效率

python 多线程有时候并不理想,主要原因是python的设计上有一个必要环节Global Interpreter Lock(GIL)全局锁。使得python还是一次性只能处理一个东西。

尽管Python完全支持多线程编程, 但是解释器的C语言实现部分在完全并行执行时并不是线程安全的。
实际上,解释器被一个全局解释器锁保护着,它确保任何时候都只有一个Python线程执行。
GIL最大的问题就是Python的多线程程序并不能利用多核CPU的优势 (比如一个使用了多个线程的计算密集型程序只会在一个单CPU上面运行)。
在讨论普通的GIL之前,有一点要强调的是GIL只会影响到那些严重依赖CPU的程序(比如计算型的)
如果你的程序大部分只会涉及到I/O,比如网络交互,那么使用多线程就很合适,因为它们大部分时间都在等待。
实际上,你完全可以放心的创建几千个Python线程, 现代操作系统运行这么多线程没有任何压力,没啥可担心的。

import threading
import time

def job():                  # 任务函数
    for _ in range(10):
        time.sleep(0.0001)

def multiThreading():      # 使用 N 个线程执行任务
    threads = []
    for i in range(10):
        t = threading.Thread(target=job,)
        t.start()
        threads.append(t)

    [t.join() for t in threads]

def normal():               # 依次执行任务 N 次
    for i in range(10):
        job()

if __name__ == '__main__':  # 多线程执行和普通顺序调用消耗时间对比
    nor_start_time = time.time()
    normal()
    print("normal_time:",time.time()-nor_start_time)

    mul_start_time = time.time()
    multiThreading()
    print("multiThreading_time:",time.time()-mul_start_time)

当任务执行10次时普通顺序调用和多线程消耗时间:

normal_time: 0.014399051666259766
multiThreading_time: 0.00513005256652832

按理来说,多线程应该比普通方法速度快10倍,因为建立了10个线程,但是结果并没有。这就是其中的GIL在作怪。

六、线程锁 Lock

1. 不使用 Lock 的情况
在一个线程执行运算修改共享内存的过程中,其他线程也可以访问该共享内存,会造成共享内存数据的混乱。

import threading

def job1():
    global A
    for i in range(10):
        A += 1
        print("Job1:",A)

def job2():
    global A
    for i in range(10):
        A += 10
        print("Job2:",A)

if __name__=="__main__":
    A = 0
    t1 = threading.Thread(target=job1)
    t2 = threading.Thread(target=job2)
    t1.start()
    t2.start()
    t2.join()
    t1.join()

结果:

Job1: 1
Job1: 2
Job1: 13
Job2: 12
Job1: 14
Job2: 24
Job1: 25
Job2: 35
Job1: 36
Job2: 46
Job1: 47
Job2: 57
Job1: 58
Job2: 68
Job1: 69
Job2: 79
Job1: 80
Job2: 90
Job2: 100
Job2: 110

2. 使用 Lock 的情况
Lock 在不同线程使用同一共享内存时,能够确保线程之间互不影响
方法:在每个线程执行运算修改共享内存之前,执行 lock.acquare() 将共享内存上锁,确保当前线程访问时,共享内存不会被其他线程访问,执行运算完毕后,使用 lock.release() 将锁打开,保证其他线程可以使用该共享内存。

import threading

def job1():
    global A,lock
    lock.acquire()       # 上锁
    for i in range(10):
        A += 1
        print("Job1:",A)
    lock.release()       # 开锁

def job2():
    global A,lock
    lock.acquire()       # 上锁
    for i in range(10):
        A += 10
        print("Job2:",A)
    lock.release()       # 开锁

if __name__=="__main__":
    A = 0
    lock = threading.Lock()
    t1 = threading.Thread(target=job1)
    t2 = threading.Thread(target=job2)
    t1.start()
    t2.start()
    t2.join()
    t1.join()

结果:

Job1: 1
Job1: 2
Job1: 3
Job1: 4
Job1: 5
Job1: 6
Job1: 7
Job1: 8
Job1: 9
Job1: 10
Job2: 20
Job2: 30
Job2: 40
Job2: 50
Job2: 60
Job2: 70
Job2: 80
Job2: 90
Job2: 100
Job2: 110

学习视频连接:?莫烦PYTHON

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值