2020.10.5 多线程

多任务可以由多进程完成,也可以由一个进程内的多线程完成。

Python的标准库提供了两个模块:_threadthreading,_thread是低级模块,threading是高级模块,对_thread完成了封装。大多数情况下,只需用到threading这个高级模块即可。

启动一个线程:把一个函数传入并创建Thread实例,然后调用start()开始执行。

复制代码
import time,threading

#新线程执行的代码
def loop():
    print('Thread %s is running'%threading.current_thread().name)
    n=0
    while n<5:
        n+=1
        print('Thread %s => %d'%(threading.current_thread().name,n))
        time.sleep(1)
    print('Thread %s ended'%threading.current_thread().name)

print('Thread %s is running...'%threading.current_thread().name)
t=threading.Thread(target=loop,name='LoopThread')
t.start()
t.join()
print('Thread %s ended'%threading.current_thread().name)
复制代码
复制代码
Thread MainThread is running...
Thread LoopThread is running
Thread LoopThread => 1
Thread LoopThread => 2
Thread LoopThread => 3
Thread LoopThread => 4
Thread LoopThread => 5
Thread LoopThread ended
Thread MainThread ended
复制代码

由于任何进程默认就会启动一个线程,故把这个线程称为主线程,即我们上文看到的MainThread,主线程又可以启动新的线程,python的threading模块有一个current_thread()函数,它永远返回当前线程的实例,子线程的名称在通过threading.Thread()创建时指定,本例中我们用LoopThread命名子线程。名字只在打印时起作用,完全没有其他作用,如果不起名字Python就自动给线程命名为Thread-1、Thread-2......

 

Lock

多线程和多进程的区别在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响

多线程中,所有变量都由所有线程共享,因此任何一个变量都可以被任意一个线程修改,因此,线程间的数据共享的最大危害在于多个线程同时修改一个变量,把内容给改乱了。

这就涉及到OS中提到的同步、互斥的概念了。

举个例子:

复制代码
import time, threading

#假定银行存款额为balance
balance=0

#先存后取 理论上balance应该不变
def change_it(n):
    global balance
    balance+=n
    balance-=n

def run_thread(n):
    for x in range(200000):
        change_it(n)

t1=threading.Thread(target=run_thread,args=(5,))
t2=threading.Thread(target=run_thread,args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
复制代码

结果:

8

 

我们创建了了两个线程,先存后取,理论上结果应该为0。但是由于线程的调度是由OS决定的,当t1、t2交替执行时,只要循环的次数足够多,balance的结果就不一定是0了。

如果我们要确保balance计算正确,就要给change_it()上一把,当某个线程开始执行change_it()时,由于该线程被我们加了锁,因此其他线程不能同时执行change_it(),只能等待,直到锁被释放,并且该线程获得锁后才能修改。由于只有一个锁,所以同一时刻最多只能有一个线程持有锁,因此就不会引发冲突。

创建一个锁通过threading.Lock()实现:

复制代码
balance=0
#创建Lock对象
lock=threading.Lock()

def run_thread(n):
    for i in range(100000):
        #获取锁
        lock.acquire()
        try:
            change_it(n) #放心修改
        finally:
            lock.release()#释放锁
复制代码

 

当多个线程同时通过lock.acquire()请求锁时,只有一个线程可以成功,然后继续执行代码,其他线程继续等待到获得锁为止。

获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将会一直等待下去,成为死线程。所以我们用try...finally保证锁一定会被释放。

 

锁的好处是确保了某段关键代码只能由一个线程从头到尾完整执行,坏处也很多,①阻止了线程的并发执行,包含锁的某段代码实际上只能以单线程模式执行,降低了效率;②存在多个锁时,线程互相请求对方持有的锁,造成死锁,只能依靠OS强制终止。

posted @   ShineLe  阅读(63)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示