python多线程学习笔记(超详细)

python

threading

多线程

一. Threading简介

首先看下面的没有用Threading的程序

import threading,time 
 
def fun(): 
    s = 0 
 for i in range(30): 
        s += i 
        time.sleep(0.1) 
    print(s) 
 
if __name__ == '__main__': 
    t = time.time() 
    fun() 
    fun() 
    print(time.time()-t) 
 
>>>  
435 
435 
6.023701906204224 
[Finished in 6.6s] 

如果使用线程会有什么样的效果呢

import threading,time 
 
def fun(): 
    s = 0 
 for i in range(30): 
        s += i 
        time.sleep(0.1) 
    print(s) 
 
if __name__ == '__main__': 
 # 创建了一个线程列表,包含2个线程 
    ths = [threading.Thread(target=fun) for i in range(2)] 
 for th in ths: 
        th.start() 
    t = time.time() 
 for th in ths: 
        th.join() 
    print(time.time()-t) 
 
 
>>>  
435 
435 
3.116874933242798 
[Finished in 3.7s] 

这说明两个线程几乎是同时进行的

二. Threading的应用进阶

  1. join(timeout)用来实现线程等待。 被调用join()方法的线程会一直阻塞调用者的线程, 直到自己结束(正常结束,或引发未处理异常), 或超出timeout的时间。
import threading,time 
 
class MyThread(threading.Thread): 
 
 def run(self): 
 for i in range(30): 
            print('threading:',i) 
            time.sleep(0.1) 
 
if __name__ == '__main__': 
    t = MyThread() 
    t.start() 
    t.join(1) 
 for i in range(10): 
        print('Main:',i) 
        time.sleep(0.1) 
 
>>>  
threading: 0 
threading: 1 
threading: 2 
主线程等待t这个线程0.1秒后也开始运行 
Main: 0 
threading: 3 
Main: 1 
threading: 4 
Main: 2 
threading: 5 
Main: 3 
threading: 6 
Main: 4 
threading: 7 
Main: 5 
threading: 8 
Main: 6 
threading: 9 
Main: 7 
Main: 8 
Main: 9 
[Finished in 2.0s] 

注意每次运行的结果都不太一样

2)daemon属性 被设定为后台运行的线程,会在主程序退出时主动自杀。 设置为后台运行线程的方法是:设置线程的daemon属性为True

import threading,time 
 
def dmn(): 
    print('dmn start...') 
    time.sleep(2) 
    print('dmn end.') 
 
def ndmn(): 
    print('ndmn start...') 
    time.sleep(1) 
    print('ndmn end.') 
 
d = threading.Thread(target=dmn) 
d.daemon = True 
n = threading.Thread(target=ndmn) 
print('start...') 
d.start() 
n.start() 
print('end.') 
 
>>> 
start... 
dmn start... 
ndmn start... 
end. 
ndmn end. 
[Finished in 1.3s] 

由上面打印的结果我们可以看到dmn线程设置为后台线程后,它的 print('dmn end.') 语句并不没有执行,这是因为后台线程在主线程结束后会自杀,所以主线程执行完后,dmn线程没能说出自己的“遗言”。 作为对比,我将daemon设为False,结果如下

... 
 
d = threading.Thread(target=dmn)  
d.daemon = False 
... 
 
>>> 
start... 
dmn start... 
ndmn start... 
end. 
ndmn end. 
dmn end. 
[Finished in 2.5s] 
线程同步
1 )指令锁 threading.Lock

acquire尝试获得锁定,进入阻塞状态。 acquire(blocking=True, timeout=-1))

release释放获得锁定(资源使用完后) release()

import threading,time,random 
 
share = 4 
lock = threading.Lock() #初始化指令锁 
 
class MyThread(threading.Thread): 
 def __init__(self,i): 
        super().__init__() 
        self.i = i 
 
 def run(self): 
 global share 
 for d in range(2): 
            lock.acquire() 
            print(share) 
            share += self.i 
            time.sleep(random.random()) 
            print('+',self.i,'=',share) 
            lock.release() 
 
if __name__ == '__main__': 
    t = MyThread(2) 
    tt = MyThread(6) 
    t.start() 
    tt.start() 
 
>>> 
4 
+ 2 = 6 
6 
+ 6 = 12 
12 
+ 2 = 14 
14 
+ 6 = 20 
[Finished in 2.9s] 

为了更好的感受指令锁的作用,将acquire和release去掉后结果如下

... 
def run(self): 
 global share  
 for d in range(2):  
 # lock.acquire()  
            print(share)  
            share += self.i  
            time.sleep(random.random())  
            print('+',self.i,'=',share)  
 # lock.release()  
... 
 
>>> 
4 
6 
+ 6 = 12 
12 
+ 2 = 18 
18 
+ 6 = 20 
+ 2 = 20 
[Finished in 2.2s] 

比较后可以知道,加了指令锁后可以清楚地知道对共享资源share操作的具体情况

2 )条件变量threading.Condition 属性

  • 实例化时,可指定锁。
  • acquire()
  • release()
  • wait(timeout=None) 释放锁,进入等待阻塞, 直到唤醒或超时。
  • notify(n=1) 唤醒等待该条件变量的线程。默认1个。
  • notify_all() 唤醒等待该条件变量的所有线程。

实现严格的依照次序操作的线程之间的通信。 典型的实例:生产者/消费者(只有生产后,才能消费)。 线程之间可以互相通知,以达到默契的配合。 条件变量可以使用默认的锁或用户创建的锁来工作。

话不多说看代码

import threading,time 
 
share = 0 
 
share_cond = threading.Condition() 
 
# 生产者 
class ProThread(threading.Thread): 
 def __init__(self): 
        super().__init__() 
        self.name = 'Produce' 
 
 def run(self): 
 global share 
 if share_cond.acquire(): 
 while True: 
 if not share:   # 若没东西了,即开始生产 
                    share += 1 
                    print(self.name,share) 
                    share_cond.notify() #唤醒消费者?这个是我自己的理解  
                share_cond.wait() 
                time.sleep(1) 
 
# 消费者 
class CustomThread(threading.Thread): 
 def __init__(self): 
        super().__init__() 
        self.name = 'Custom' 
 
 def run(self): 
 global share 
 if share_cond.acquire(): 
 while True: 
 if share: 
                    share -= 1 # 若有东西就买买买 
                    print(self.name,share) 
                    share_cond.notify() #唤醒生产者,同上,仅是个人理解,如有错请告知,谢谢 
                share_cond.wait() 
                time.sleep(1) 
 
if __name__ == '__main__': 
    t  = ProThread() 
    tt = CustomThread() 
    t.start() 
    tt.start() 
 
>>> 
Produce 1 
Custom 0 
Produce 1 
Custom 0 
Produce 1 
Custom 0 
... 
... 
... 

上面的结果会一直重复执行下去

3 ) 信号量threading.Semaphore

属性

  • 实例化时,指定使用量。
  • 其内置计数器,锁定时+1, 释放时-1,计数器为0则阻塞。
  • acquire(blocking=True,timeout=None)
  • release()释放锁。
import threading,time 
 
sema = threading.Semaphore(2) 
 
class MyThread(threading.Thread): 
 def __init__(self,name): 
        super().__init__() 
        self.name = name 
 
 def run(self): 
 if sema.acquire(): 
            print(self.name,'Had got resource.') 
            time.sleep(1) 
        sema.release() 
        print(self.name,'Had released resource.') 
 
if __name__ == '__main__': 
    ths = [MyThread(str(i)+'Sema') for i in range(5)] 
 for th in ths: 
        th.start() 
 
>>> 
0Sema Had got resource. 
1Sema Had got resource. 
2Sema Had got resource. 
1Sema Had released resource. 
3Sema Had got resource. 
0Sema Had released resource. 
3Sema Had released resource. 
4Sema Had got resource. 
2Sema Had released resource. 
4Sema Had released resource. 
[Finished in 3.6s] 

4 ) 线程通信threading.Event

  • 其管理一个内部标志.实现一个线程,唤醒其它线程。
  • set() 设置内部标志为True
  • clear() 设置内部标志为False
  • wait(timeout) 阻塞线程,到内部标志为True。
import threading,time 
 
event = threading.Event() 
 
class MyThreadWait(threading.Thread): 
 def run(self): 
        self.name = 'Wait Thread' 
        print(self.name,"Wait...") 
        event.wait() 
        print(self.name,"Start...") 
        event.clear() 
 
class MyThreadMain(threading.Thread): 
 def run(self): 
        time.sleep(3) 
        print('Main thread set event flag!') 
        event.set() 
 
if __name__ == '__main__': 
    thw = MyThreadWait() 
    thm = MyThreadMain() 
    thw.start() 
    thm.start() 
 
>>> 
Wait Thread Wait... 
Main thread set event flag! 
Wait Thread Start... 
[Finished in 3.6s] 

好了,大概就是这些了,其他的以后再补充,另外感谢麦子学院提供的免费课程~~~真心不是打广告

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏李航的专栏

Shell 主要逻辑源码级分析:SHELL 运行流程 (1)

分享一下在学校的时候分析shell源码的一些收获,帮助大家了解shell的一个工作流程,从软件设计的角度,看看shell这样一个历史悠久的软件的一些设计优点和缺...

2.2K00
来自专栏枕边书

搭建自己的PHP框架心得(二)

续言 对于本次更新,我想说: 本框架由本人挑时间完善,而我还不是PHP大神级的人物,所以框架漏洞难免,求大神们指出。 本框架的知识点应用都会写在博客里,大家有什...

26280
来自专栏haifeiWu与他朋友们的专栏

otto框架解析

otto是square公司出的一个事件库(pub/sub模式),用来简化应用程序之间的通讯。

12020
来自专栏老马说编程

(72) 显式条件 / 计算机程序的思维逻辑

上节我们介绍了显式锁,本节介绍关联的显式条件,介绍其用法和原理。显式条件也可以被称做条件变量、条件队列、或条件,后文我们可能会交替使用。 用法 基本概念和方法...

19960
来自专栏技术博文

PHP编程风格规范

本规范由 EasyChen 借鉴 SINA网络应用开发部《C++开发规范》和互动技术部《PHP4开发规范》,以及phpDocument规范 整理出的开发规范。我...

40170
来自专栏林德熙的博客

C#判断文件是否被混淆

可以使用混淆工具对一个DLL 和 exe 进行混淆。 但是如何知道一个文件是否已经混淆了。 在发布之前,需要知道是不是有文件忘了混淆。

26520
来自专栏吴伟祥

Jmockdata随机模拟 Java 数据插件

     Jmockdta是一款实现模拟JAVA类型或对象的实例化并随机初始化对象的数据的工具框架。

11720
来自专栏小勇DW3

自己手动写代码实现数据库连接池

池:一个高级的集合体(集合存储元素 + 管理方式–>提高效率),是对外提供同一种类型对象的集合,如(线程池、数据库连接池)  特性:复用性(每条连接可重复使用)...

17430
来自专栏木宛城主

Unity应用架构设计(7)——IoC工厂理念先行

一谈到 『IoC』,有经验的程序员马上会联想到控制反转,将创建对象的责任反转给工厂。IoC是依赖注入 『DI』 的核心,大名鼎鼎的Spring框架就是一个非常...

29370
来自专栏眯眯眼猫头鹰的小树杈

猫头鹰的深夜翻译:理解java的classloader

Java ClassLoader是java运行系统中一个至关重要但是经常被忽略的组件。它负责在运行时寻找并加载类文件。创建自定义的ClassLoader可以彻底...

14440

扫码关注云+社区

领取腾讯云代金券