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异常学习笔记
Feb 03 Python
python中循环语句while用法实例
May 16 Python
Python爬取国外天气预报网站的方法
Jul 10 Python
Python利用ansible分发处理任务
Aug 04 Python
Python脚本实现12306火车票查询系统
Sep 30 Python
基于python3实现socket文件传输和校验
Jul 28 Python
详解Python网络框架Django和Scrapy安装指南
Apr 01 Python
Pyqt5如何让QMessageBox按钮显示中文示例代码
Apr 11 Python
Python统计分析模块statistics用法示例
Sep 06 Python
关于PyCharm安装后修改路径名称使其可重新打开的问题
Oct 20 Python
Python实现Appium端口检测与释放的实现
Dec 31 Python
Python页面加载的等待方式总结
Feb 28 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 移除数组重复元素的一点说明
2008/11/27 PHP
PHPMailer的主要功能特点和简单使用说明
2014/02/17 PHP
Zend Framework缓存Cache用法简单实例
2016/03/19 PHP
Jquery实现带动画效果的经典二级导航菜单
2013/03/22 Javascript
js函数排序的实例代码
2013/07/01 Javascript
JS使用replace()方法和正则表达式进行字符串的搜索与替换实例
2014/04/10 Javascript
jQuery事件之键盘事件(ctrl+Enter回车键提交表单等)
2014/05/11 Javascript
jQuery中prepend()方法使用详解
2015/08/11 Javascript
JavaScript 不支持 indexof 该如何解决
2016/03/30 Javascript
Bootstrap每天必学之导航组件
2016/04/25 Javascript
jQuery使用ajax方法解析返回的json数据功能示例
2017/01/10 Javascript
开发一个Parcel-vue脚手架工具(详细步骤)
2018/09/22 Javascript
vue动态绑定class选中当前列表变色的方法示例
2018/12/19 Javascript
利用Vue实现一个markdown编辑器实例代码
2019/05/19 Javascript
浅谈监听单选框radio改变事件(和layui中单选按钮改变事件)
2019/09/10 Javascript
在react中使用vue的状态管理的方法示例
2020/05/02 Javascript
vue $router和$route的区别详解
2020/12/02 Vue.js
[02:28]DOTA2亚洲邀请赛附加赛 RECAP赛事回顾
2015/01/29 DOTA
[01:35:13]DOTA2-DPC中国联赛 正赛 DLG vs PHOENIX BO3 第一场 1月18日
2021/03/11 DOTA
python使用电子邮件模块smtplib的方法
2016/08/28 Python
Python实现的弹球小游戏示例
2017/08/01 Python
TensorFlow实现卷积神经网络
2018/05/24 Python
Django自定义模板过滤器和标签的实现方法
2019/08/21 Python
Python urllib.request对象案例解析
2020/05/11 Python
python 爬取小说并下载的示例
2020/12/07 Python
基本款天堂:Everlane
2017/05/13 全球购物
NFL墨西哥官方商店:Tienda NFL
2017/11/28 全球购物
世界各地的旅游、观光和活动:Isango!
2019/10/29 全球购物
儿科护士实习自我鉴定
2013/10/17 职场文书
自考毕业生自我鉴定
2013/11/04 职场文书
平面设计师的工作职责
2013/11/21 职场文书
计算机专业毕业生自荐书
2014/06/02 职场文书
社区低保工作总结2015
2015/07/23 职场文书
一文搞懂Python Sklearn库使用
2021/08/23 Python
德劲DE1105机评
2022/04/05 无线电
MySQL普通表如何转换成分区表
2022/05/30 MySQL