Python多线程中阻塞(join)与锁(Lock)使用误区解析


Posted in Python onApril 27, 2018

关于阻塞主线程

join的错误用法

Thread.join() 作用为阻塞主线程,即在子线程未返回的时候,主线程等待其返回然后再继续执行.

join不能与start在循环里连用
以下为错误代码,代码创建了5个线程,然后用一个循环激活线程,激活之后令其阻塞主线程.

threads = [Thread() for i in range(5)]
for thread in threads:
 thread.start()
 thread.join()

执行过程:

1. 第一次循环中,主线程通过start函数激活线程1,线程1进行计算.
2. 由于start函数不阻塞主线程,在线程1进行运算的同时,主线程向下执行join函数.
3. 执行join之后,主线程被线程1阻塞,在线程1返回结果之前,主线程无法执行下一轮循环.
4. 线程1计算完成之后,解除对主线程的阻塞.
5. 主线程进入下一轮循环,激活线程2并被其阻塞…

如此往复,可以看出,本来应该并发的五个线程,在这里变成了顺序队列,效率和单线程无异.

join的正确用法

使用两个循环分别处理startjoin函数.即可实现并发.

threads = [Thread() for i in range(5)]
for thread in threads:
 thread.start()
for thread in threads:
 thread.join()

time.sleep代替join进行调试

之前在一些项目里看到过这样的代码,使用time.sleep代替join手动阻塞主线程.
在所有子线程返回之前,主线程陷入无线循环而不能退出.

for thread in threads:
 thread.start()
while 1:
 if thread_num == 0:
 break
 time.sleep(0.01)

关于线程锁(threading.Lock)

单核CPU+PIL是否还需要锁?

非原子操作 count = count + 1 理论上是线程不安全的.
使用3个线程同时执行上述操作改变全局变量count的值,并查看程序执行结果.
如果结果正确,则表示未出现线程冲突.

使用以下代码测试

# -*- coding: utf-8 -*-

import threading
import time
count = 0

class Counter(threading.Thread):
 def __init__(self, name):
 self.thread_name = name
 super(Counter, self).__init__(name=name)

 def run(self):
 global count
 for i in xrange(100000):
  count = count + 1


counters = [Counter('thread:%s' % i) for i in range(5)]
for counter in counters:
 counter.start()

time.sleep(5)
print 'count=%s' % count

运行结果:

count=275552

事实上每次运行结果都不相同且不正确,这证明单核CPU+PIL仍无法保证线程安全,需要加锁.

加锁后的正确代码:

# -*- coding: utf-8 -*-

import threading
import time

count = 0
lock = threading.Lock()


class Counter(threading.Thread):
 def __init__(self, name):
 self.thread_name = name
 self.lock = threading.Lock()
 super(Counter, self).__init__(name=name)

 def run(self):
 global count
 global lock
 for i in xrange(100000):
  lock.acquire()
  count = count + 1
  lock.release()


counters = [Counter('thread:%s' % i) for i in range(5)]

for counter in counters:
 counter.start()

time.sleep(5)
print 'count=%s' % count

结果:

count=500000

注意锁的全局性

这是一个简单的Python语法问题,但在逻辑复杂时有可能被忽略.
要保证锁对于多个子线程来说是共用的,即不要在Thread的子类内部创建锁.

以下为错误代码

# -*- coding: utf-8 -*-

import threading
import time

count = 0
# lock = threading.Lock() # 正确的声明位置

class Counter(threading.Thread):
 def __init__(self, name):
 self.thread_name = name
 self.lock = threading.Lock() # 错误的声明位置
 super(Counter, self).__init__(name=name)

 def run(self):
 global count
 for i in xrange(100000):
  self.lock.acquire()
  count = count + 1
  self.lock.release()


counters = [Counter('thread:%s' % i) for i in range(5)]

for counter in counters:
 print counter.thread_name
 counter.start()

time.sleep(5)
print 'count=%s' % count

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python ORM框架SQLAlchemy学习笔记之数据查询实例
Jun 10 Python
Python使用PIL库实现验证码图片的方法
Mar 11 Python
Python基于identicon库创建类似Github上用的头像功能
Sep 25 Python
matplotlib绘制动画代码示例
Jan 02 Python
Python使用requests发送POST请求实例代码
Jan 25 Python
tensorflow中next_batch的具体使用
Feb 02 Python
Pandas 数据处理,数据清洗详解
Jul 10 Python
解决python中遇到字典里key值为None的情况,取不出来的问题
Oct 17 Python
flask框架json数据的拿取和返回操作示例
Nov 28 Python
python 非线性规划方式(scipy.optimize.minimize)
Feb 11 Python
使用jupyter notebook运行python和R的步骤
Aug 13 Python
用python进行视频剪辑
Nov 02 Python
python队列queue模块详解
Apr 27 #Python
浅谈tensorflow1.0 池化层(pooling)和全连接层(dense)
Apr 27 #Python
python线程中同步锁详解
Apr 27 #Python
python数字图像处理之高级形态学处理
Apr 27 #Python
python线程池threadpool实现篇
Apr 27 #Python
python数字图像处理之骨架提取与分水岭算法
Apr 27 #Python
python多线程之事件Event的使用详解
Apr 27 #Python
You might like
PHP调用MySQL的存储过程的实现代码
2008/08/12 PHP
php实现四舍五入的方法小结
2015/03/03 PHP
深入浅析用PHP实现MVC
2016/03/02 PHP
PHP中如何防止外部恶意提交调用ajax接口
2016/04/11 PHP
lnmp安装多版本PHP共存的方法详解
2018/08/02 PHP
PHP文件打开关闭及读写操作示例解析
2020/08/06 PHP
图片完美缩放
2006/09/07 Javascript
如何让页面在打开时自动刷新一次让图片全部显示
2012/12/17 Javascript
jQuery把表单元素变为json对象
2013/11/06 Javascript
浅析jquery的作用与优势
2013/12/02 Javascript
JSON+HTML实现国家省市联动选择效果
2014/05/18 Javascript
jQuery+ajax实现鼠标单击修改内容的方法
2014/06/27 Javascript
JavaScript获取网页、浏览器、屏幕高度和宽度汇总
2014/12/18 Javascript
css如何让浮动元素水平居中
2015/08/07 Javascript
JavaScript的String字符串对象常用操作总结
2016/05/26 Javascript
JS实现一个简单的日历
2017/02/22 Javascript
jquery实现左右轮播切换效果
2018/01/01 jQuery
Vue2.0用户权限控制解决方案的示例
2018/02/10 Javascript
JS计算斐波拉切代码实例
2019/09/12 Javascript
微信小程序自定义菜单切换栏tabbar组件代码实例
2019/12/30 Javascript
布同 统计英文单词的个数的python代码
2011/03/13 Python
Python中%r和%s的详解及区别
2017/03/16 Python
Python利用公共键如何对字典列表进行排序详解
2018/05/19 Python
把vgg-face.mat权重迁移到pytorch模型示例
2019/12/27 Python
Python pymysql模块安装并操作过程解析
2020/10/13 Python
Python字符串查找基本操作代码案例
2020/10/27 Python
Python .py生成.pyd文件并打包.exe 的注意事项说明
2021/03/04 Python
ShellScript面试题一则-ShellScript编程
2014/03/05 面试题
货代行业个人求职简历的自我评价
2013/10/22 职场文书
学校庆元旦歌咏比赛主持词
2014/03/18 职场文书
作风建设演讲稿
2014/05/23 职场文书
党员个人对照检查材料思想汇报
2014/09/16 职场文书
学生实习证明模板汇总
2014/09/25 职场文书
Python实现socket库网络通信套接字
2021/06/04 Python
Python将CSV文件转化为HTML文件的操作方法
2021/06/30 Python
Mysql数据库手动及定时备份步骤
2021/11/07 MySQL