python4出来吗_python之旅(四)

上篇文章没想到能用那么多篇幅,主要是我自己的小实验做的太多了,终于到了多线程环节,先来一波单线程的示例:

def sing(name):

print('演唱《%s》' % name)

sing_time = 2

sleep(sing_time)

def main():

start = time()

sing('演员')

sing('绅士')

end = time()

print('总共耗费了%d秒' % (end - start))

if __name__ == '__main__':

main()

效果不用说,顺序执行自然是耗时较长,那么我们可以用多线程来节约时间,提高程序的执行效率。

目前我了解到的线程写法有两种,刚开始我是觉得第一种优于第二种,但是仔细想一下,可能这两种写法并没有优劣之分,这个一会儿说,先看代码。

from threading import Thread

from time import time, sleep

def sing(name):

print('演唱《%s》' % name)

sing_time = 2

sleep(sing_time)

def main():

start = time()

t = Thread(target = sing, args = ('演员', ))

t2 = Thread(target = sing, args = ('绅士', ))

t.start()

t2.start()

t2.join()

end = time()

print('总共耗费了%d秒' % (end - start))

if __name__ == '__main__':

main()

第一种写法就是通过Thread方法来创建线程,start、join这些方法也算是老朋友了,上篇文章都接触过。

如果多线程执行相同的方法,这种写法是比较便捷的。

class sing(Thread):

def __init__(self, name):

super().__init__()

self._name = name

def run(self):

print('演唱《%s》' % self._name)

sing_time = 2

sleep(sing_time)

def main():

start = time()

sing('演员').start()

t = sing('绅士')

t.start()

t.join()

end = time()

print('总共耗费了%d秒' % (end - start))

if __name__ == '__main__':

main()

这是第二种写法,继承Thread类,重写run方法,这和java中继承Thread就比较类似了,run方法的内容就是线程将要执行的任务,对于多线程执行不同任务的时候,这种写法可能会更好一些。

那么线程间如何共享数据呢?提到这里就又让我想起了学java时学到的线程知识,假设我们创建了一个全局变量,它作为共享变量,每个线程对其执行++操作,那么线程会先读取它的值,写入本地内存(线程的本地内存),执行自增操作,再回写到主存中,那么当两个线程并行的时候,同时读取到同一个变量,他们读取到的值是相同的,同时自增1,得到的结果也是相同的,最终结果就可想而知了,这是一个比较简单的线程安全问题,为了避免这种问题,我们要保证线程同步。

先尝试一波不安全的线程代码:

from threading import Thread

from time import time, sleep

common = 0

def add():

global common

c = common + 10

sleep(0.02)

common = c

def main():

global common

while common == 10000 or common == 0:

common = 0

list0 = []

for x in range(1000):

t = Thread(target = add, args = ())

t.start()

list0.append(t)

for t in list0:

t.join()

print(common)

if __name__ == '__main__':

main()

假设线程每次执行+10操作耗费0.02秒,可以看到1000个线程每个线程都+10最终结果居然不等于10000。

本想就着现有的代码改良一波,想不到直接报错,查了一下,貌似是由于并发线程太多被检测到,然后直接不让执行了,行吧。

from threading import Thread, Lock

from time import time, sleep

common = 0

lock = Lock()

def add():

global common, lock

#lock.acquire()

c = common + 10

sleep(0.02)

common = c

#lock.release()

def main():

global common

common = 0

list0 = []

for x in range(100):

t = Thread(target = add, args = ())

t.start()

list0.append(t)

for t in list0:

t.join()

print(common)

if __name__ == '__main__':

main()

现在加锁的代码注掉了,依然是错误的计算结果,当正常加锁后,结果正确。

实际上释放锁的代码最好写在finally中,不然可能真正写项目的时候要死锁的。

但是,python多线程并不能发挥多核cpu的优势,原因是存在一个全局解释器锁,想执行得先获取这个锁,执行100条字节码,解释器自动释放这个锁让别的线程有机会执行,这是我现在学习的这套教程里讲到的。

那么也就是说线程不仅要获取我们自定义的线程锁,还需要获取全局解释器锁才能顺利执行100条字节码,应该是这个意思吧。所以说在python中线程并不能真正的并行?同一时刻,只有一个线程在执行?但是运用了多线程的确缩短了执行时间,看来还有一些进阶一点的东西需要以后慢慢探索。

资料中还讲到了关于效率的问题,这一部分的话,我本人没有实践经验,写出来也是照抄照搬,那就没多大意思了,所以暂且略过。

然后资料中提到了协程,也就是单线程+异步io,这玩意和单线程多路io复用说的应该是一回事吧?不是特别清楚,这一块我本人了解的比较浅薄,大佬说之后会出文章介绍,本篇文章中提到了单线程避免了线程切换和加锁的消耗,这个倒是挺好理解的哈。

这个系列写到了第四章还没有把大佬的教程推荐给大家,这一系列教程是我在微信公众号中偶然发现的,甚是喜欢,名字叫做Python-100-Days-master。

地址:https://github.com/jarodHAN/Python-100-Days-master

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值