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 相关文章推荐
在Django的视图中使用数据库查询的方法
Jul 16 Python
详解Python中dict与set的使用
Aug 10 Python
动感网页相册 python编写简单文件夹内图片浏览工具
Aug 17 Python
一文总结学习Python的14张思维导图
Oct 17 Python
Python爬虫实现验证码登录代码实例
May 10 Python
Python学习笔记之文件的读写操作实例分析
Aug 07 Python
Django框架中间件定义与使用方法案例分析
Nov 28 Python
python matplotlib中的subplot函数使用详解
Jan 19 Python
Pycharm+Python工程,引用子模块的实现
Mar 09 Python
python实现学生通讯录管理系统
Feb 25 Python
Python基础之教你怎么在M1系统上使用pandas
May 08 Python
一文搞懂python异常处理、模块与包
Jun 26 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数据库连接时容易出错的特殊符号问题
2010/09/01 PHP
PHP和JAVA中的重载(overload)和覆盖(override) 介绍
2012/03/01 PHP
浅析get与post的一些特殊情况
2014/07/28 PHP
从刷票了解获得客户端IP的方法
2015/09/21 PHP
php中替换字符串函数strtr()和str_repalce()的用法与区别
2016/11/25 PHP
laravel自定义分页的实现案例offset()和limit()
2019/10/15 PHP
Javascript结合css实现网页换肤功能
2009/11/02 Javascript
Prototype源码浅析 String部分(三)之HTML字符串处理
2012/01/15 Javascript
Node.js中AES加密和其它语言不一致问题解决办法
2014/03/10 Javascript
JavaScript操作DOM元素的childNodes和children区别
2015/04/01 Javascript
浏览器中url存储的JavaScript实现
2015/07/07 Javascript
JavaScript高级程序设计(第三版)学习笔记6、7章
2016/03/11 Javascript
jQuery验证插件validate使用详解
2016/05/11 Javascript
Javascript日期格式化format函数的使用方法
2016/08/30 Javascript
jQuery插件实现可输入和自动匹配的下拉框
2016/10/24 Javascript
js实现加载更多功能实例
2016/10/27 Javascript
详解vue-cli + webpack 多页面实例应用
2017/04/25 Javascript
vue中各组件之间传递数据的方法示例
2017/07/27 Javascript
jQuery使用zTree插件实现可拖拽的树示例
2017/09/23 jQuery
利用angular、react和vue实现相同的面试题组件
2018/02/19 Javascript
React Router v4 入坑指南(小结)
2018/04/08 Javascript
layer弹出层全屏及关闭方法
2018/08/17 Javascript
layui table 表格上添加日期控件的两种方法
2019/09/28 Javascript
vuex+axios+element-ui实现页面请求loading操作示例
2020/02/02 Javascript
[02:56]DOTA2亚洲邀请赛 VG出场战队巡礼
2015/02/07 DOTA
[55:25]VGJ.T vs Optic Supermajor小组赛D组 BO3 第三场 6.3
2018/06/04 DOTA
python实现文件路径和url相互转换的方法
2015/07/06 Python
Django CBV与FBV原理及实例详解
2019/08/12 Python
详解使用PyInstaller将Pygame库编写的小游戏程序打包为exe文件
2019/08/23 Python
python使用配置文件过程详解
2019/12/28 Python
python框架Django实战商城项目之工程搭建过程图文详解
2020/03/09 Python
python模拟实现分发扑克牌
2020/04/22 Python
Python爬取豆瓣数据实现过程解析
2020/10/27 Python
幼儿园亲子活动感想
2015/08/07 职场文书
关于MybatisPlus配置双数据库驱动连接数据库问题
2022/01/22 Java/Android
Win10/Win11 任务栏替换成经典样式
2022/04/19 数码科技