Python3.x:threading module多线程案例

这里说一个打击人的消息,python的多线程是假的多线程

详细请看链接:UnstandingGIL.pdf 密码:8jo3

核心意思就是无论你开启几个线程,你有多少个CPU,Python解释器在执行的时候一个时刻只会执行一个线程,这就叫做GIL

WTF?可能你会问:
那还要什么多线程,再多线程也就跑一个线程算什么多线程?
为什么我使用了多线程之后程序运行消耗时间减少了,这不是有效么?
为什么我使用多线程时间没有减少,反而增加了?

下面就先解答一下这些问题

既然同一时刻只会执行一个线程,为什么还要有多线程?

这里就要分两种情况,看看你的代码是计算密集型还是IO密集型了,计算密集型顾名思义就是需要大量计算的代码,绝大部分时间都消耗在CPU计算上了,这个时候啊,不论你开多少线程,他用的时间都是这么多,甚至比原来时间还长,因为全局解释器锁(GIL)一个时刻只让你跑一个线程啊,大部分计算密集型任务你分了很多线程但是依然会按照代码顺序线性执行,甚至因为代码变得亢长了,反而使执行时间增加了

但是IO密集型就不一样了,90%以上的时间都花费在网络、硬盘、输入输出上了,CPU执行完命令之后剩下的就不需要在CPU中跑了,就可以释放内存来跑下一条命令了,所以对于IO密集型任务,多线程还是会起到很大的作用的,所以说有的使用了多线程之后还是能大大提高效率的

上面也加了一些自己的理解,如果你想更深入的理解,就需要接触一些更低层的东西,这些这里就暂时不做研究下面就看一个简单的多线程案例

import threading
import time


def express1(n):
    print('this is', n)
    time.sleep(2)


def express2(m):
    print('this is', m)
    time.sleep(2)


# s1 = threading.Thread(target=express1, args=('one', ))
# s2 = threading.Thread(target=express2, args=('two', ))
# s1.start()
# s2.start()


express1('one')
express2('two')

这段代码先打印了‘this is one’,然后间隔两秒又打印了‘this is two’再等待两秒程序结束,可以看到这是线性执行的,整个程序花费了四秒左右,接下来看看如何实现并发

import threading
import time


def express1(n):
    print('this is', n)
    time.sleep(2)


def express2(m):
    print('this is', m)
    time.sleep(2)


s1 = threading.Thread(target=express1, args=('one', ))
s2 = threading.Thread(target=express2, args=('two', ))
s1.start()
s2.start()


# express1('one')
# express2('two')

这样,就会同时打印‘this is one’和‘this is two’,整个程序花费了两秒左右,可以很明显的看出来express1express2是一起执行的,这样就实现了多线程

上面是一种实现方式,threadingmodule还提供了另一种实现方式:

import threading
import time


# 继承类threading.Thread
class MyThread(threading.Thread):
    def __init__(self, n):
        # 这里要继承构造函数
        super(MyThread, self).__init__()
        # 可以定义自己的实例变量
        self.n = n

    def run(self):
        print('running task', self.n)
        time.sleep(2)


t1 = MyThread('t1')
t2 = MyThread('t2')

t1.start()
t2.start()

这样就实现了通过继承父类的方式使用多线程,这里注意的几个点:

1.继承类之后还要继承构造函数

2.这里的run函数是重写的,所以说t1.start()之后才会自动调用run函数,具体详细细节可以自行查看源码

这样开一条线程就要写一行代码,我要是开50、100个线程不是要写很多行代码,当然不用,这里我觉得应该没什么难度,写个小循环不就行了

import threading
import time


# 继承类threading.Thread
class MyThread(threading.Thread):
    def __init__(self, n):
        # 这里要继承构造函数
        super(MyThread, self).__init__()
        # 可以定义自己的实例变量
        self.n = n

    def run(self):
        print('running task', self.n)
        time.sleep(2)


# t1 = MyThread('t1')
# t2 = MyThread('t2')

for i in range(50):
    t = MyThread(i)
    t.start()


# t1.start()
# t2.start()

那我们能利用timemodule计算一下这50个线程花费了多长时间么?肯定也是可以的,但是,这里有一些问题需要注意,先说原理,明白发生了什么就知道了问题所在和解决办法

我们开启了50个线程,但是这个程序不单有50个线程,还有一个主线程,假如我们在循环前后各加入一句获得当前时间的代码,那么这两句代码就是在主线程里,然后主线程跑主线程的,子线程跑子线程的,因为子线程需要的时间更长,所以主线程先跑完,也就是说计算得出的时间并不是这些子线程跑完的时间,主线程不会等待子线程跑完自己才去跑,而是大家一起跑,都跑完程序就结束

那我们如何计算这些线程执行所花费的时间呢?这个时候就有一个方法
join()
能够使主线程等待子线程的执行完毕才会继续主线程,下面看一段代码

import threading
import time


# 继承类threading.Thread
class MyThread(threading.Thread):
    def __init__(self, n):
        # 这里要继承构造函数
        super(MyThread, self).__init__()
        # 可以定义自己的实例变量
        self.n = n

    def run(self):
        print('running task', self.n)
        time.sleep(2)


# 得到开始时间
start_time = time.time()
# 声明空列表
threads = []

# 循环开启50线程
for i in range(50):
    t = MyThread(i)
    t.start()
    # 线程实例放入列表中
    threads.append(t)
    
# 对线程实例所在列表再循环
for res in threads:
    # 对每一个实例使用jion()方法
    res.join()   
# 获得结束时间
end_time = time.time()
# 计算花费时间
spend_time = start_time - end_time
# 打印花费时间
print(spend_time)

这里要注意的点就是join()方法不能在线程启动之后就使用,这样的话50个线程就会变成串行,必须要等50个线程全部启动之后才能使用join()方法,这里就用到了一个小技巧新建一个空列表,再将50个线程实例放进去,这样后面就可以实现对这50个线程使用join()方法了

最后,我们就可以得到我们想要的50个线程执行所花费的时间

下一篇:Python3.x:threading module区分主/子线程与守护进程简析

转载请注明出处

python自学技术互助扣扣群:670402334

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,063评论 6 510
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,805评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,403评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,110评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,130评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,877评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,533评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,429评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,947评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,078评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,204评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,894评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,546评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,086评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,195评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,519评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,198评论 2 357

推荐阅读更多精彩内容