当前位置: 主页 > Python语言

python 多线程并发锁-线程是并发还是并行

发布时间:2023-02-09 11:28   浏览次数:次   作者:佚名

多线程同步(一)——锁

在多线程编程中,总会有一些函数或者代码块是我们不希望被多个线程同时执行的。 线程之间的竞争将导致数据更改无法正确发生。

为了控制同一时间只有一个进程访问某个资源,有一些同步的方法。 如锁、信号量等。其中,锁比较容易理解,是最简单、最底层的机制。

锁有locked和unlocked两种状态,同时只支持两种方法,获取锁和释放锁。 线程竞争锁python 多线程并发锁,只有竞争过锁的线程才能执行。 执行或执行一段时间后,释放锁,交给其他线程。 这样可以确保获取锁和释放锁之间的代码一次只能由一个线程执行。

我们先举一个多线程读写出错的情况来说明线程竞争导致的读写出错。 即典型的全局变量(race condiion)的线程安全问题

%%time
import threading
# import time
# 线程轮流修改一个全局变量,看看变量的结果是否正常
# 做个简单的,模拟类似银行转账的操作,往一个账户上+10元,在-10元,按理说最后账户余额,无论执行多少次都是0。这是期望的运行结果
# 执行了几次没看到期望结果,原来是因为一进一出互相抵消了,所以说即使中间出错最终结果也是对的。
account=0
def test_thread(id,repeat_num):
    for _ in range(repeat_num):
        global account
        account+=1

线程是并发还是并行_线程并发拷贝程序_python 多线程并发锁

# time.sleep(0.1) # account-=1 # print('thread{0},acount:{1}'.format(id,account)) threads=[] def main(): # 启动10个线程,每个线程执行10次+1 for i in range(10): # 测试了几次终于找到了合适的会出错的数字,我这个性能的电脑 (i7 8700k)执行1000000次级别以上就很容易出错了。 t=threading.Thread(target=test_thread,args=(i,1000000)) threads.append(t) t.start() for t in threads: t.join() print('final account:',account) if __name__=='__main__': main()

python 多线程并发锁_线程是并发还是并行_线程并发拷贝程序

final account: 4214990
Wall time: 611 ms

原来是多线程竞争导致的全局变量安全问题难以检测,尝试了n次也没有报错。最后将每个线程+1的次数调整为1000000,最后报错更多明显的

同样是上面的代码,我们用锁对其进行改造,使其成为线程安全的程序:

%%time
import threading
account=0
# 创建一个锁的实例
lock=threading.Lock()
def test_thread(id,repeat_num):
    for _ in range(repeat_num):
        global account
        lock.acquire()
#         这段对accout全局变量进行读写的操作需要加锁

线程并发拷贝程序_python 多线程并发锁_线程是并发还是并行

account+=1 lock.release() threads=[] def main(): for i in range(10): t=threading.Thread(target=test_thread,args=(i,1000000)) threads.append(t) t.start() for t in threads: t.join() print('final account:',account) if __name__=='__main__': main()

final account: 10000000

python 多线程并发锁_线程并发拷贝程序_线程是并发还是并行

Wall time: 37.9 s

锁定后,运行结果正常,但速度慢了很多。 可以看出锁定步骤消耗了很多时间。

使用上下文管理,python2.5以上,可以不用acquire()和release()方法,使用with语句进一步简化代码

threading模块的对象Lock、Rlock、Condition、Semaphore、BoundedSemaphore都包含上下文管理器python 多线程并发锁,即都可以使用with语句

with语句是上下文管理器带来的语法糖。 它的作用是为一段代码添加上下文。 通常用于异常处理,比如file with open()... 这个函数就是在异常发生的时候自动帮我们关闭文件句柄,这样就可以不用你写异常处理就可以完成文件操作了。 代码更清晰一些。 另一种是多线程锁定的情况。

如下,加锁部分用with语句处理'

%%time
import threading
account=0
# 创建一个锁的实例
lock=threading.Lock()
def test_thread(id,repeat_num):
    for _ in range(repeat_num):
        global account
#         with语句加锁

python 多线程并发锁_线程并发拷贝程序_线程是并发还是并行

with lock: account+=1 threads=[] def main(): for i in range(10): t=threading.Thread(target=test_thread,args=(i,1000000)) threads.append(t) t.start() for t in threads: t.join() print('final account:',account) if __name__=='__main__': main()

final account: 10000000
Wall time: 37 s