python之多线程 threading.Lock() 和 threading.RLock()
0.目录
2. threading.Lock() 的必要性
3.观察block
4.threading.RLock() 的应用场景
1.参考
Thread Synchronization Mechanisms in Python
count += 1 不是原子操作,三步操作可能被中断,通过lock将三步操作“封装”为一步操作,要么执行,要么不执行。
counter = 0 def process_item(item):
global counter
... do something with item ...
counter += 1 # The reason for this is that the increment operation is actually executed in three steps;
#first, the interpreter fetches the current value of the counter,
# then it calculates the new value,
# and finally, it writes the new value back to the variable.
Atomic Operations #
The simplest way to synchronize access to shared variables or other resources is to rely on atomic operations in the interpreter.
An atomic operation is an operation that is carried out in a single execution step, without any chance that another thread gets control.
What kinds of global value mutation are thread-safe?
python的原子操作
A global interpreter lock (GIL) is used internally to ensure that only one thread runs in the Python VM at a time. In general, Python offers to switch among threads only between bytecode instructions; how frequently it switches can be set via sys.setcheckinterval(). Each bytecode instruction and therefore all the C implementation code reached from each instruction is therefore atomic from the point of view of a Python program. In theory, this means an exact accounting requires an exact understanding of the PVM bytecode implementation. In practice, it means that operations on shared variables of built-in data types (ints, lists, dicts, etc) that “look atomic” really are. For example, the following operations are all atomic (L, L1, L2 are lists, D, D1, D2 are dicts, x, y are objects, i, j are ints): L.append(x)
L1.extend(L2)
x = L[i]
x = L.pop()
L1[i:j] = L2
L.sort()
x = y
x.field = y
D[x] = y
D1.update(D2)
D.keys() These aren’t: i = i+1
L.append(L[-1])
L[i] = L[j]
D[x] = D[x] + 1 Operations that replace other objects may invoke those other objects’ __del__() method when their reference count reaches zero, and that can affect things. This is especially true for the mass updates to dictionaries and lists. When in doubt, use a mutex!
2. threading.Lock() 的必要性
#!usr/bin/env python
#coding:utf-8
import sys
import time
import random
import logging import threading
import Queue lock = threading.Lock() #'function-call ownership'
rlock = threading.RLock() #thread ownership logging.basicConfig(level=logging.DEBUG,
format = '%(asctime)s - %(threadName)-10s - %(levelname)s - %(message)s')
logger = logging.getLogger() count = 0 class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self) def run(self):
global count for i in range(100):
count += 1
logger.debug('{} finished, count is {}'.format(self.name, count)) def main():
logger.debug('initial count: {}'.format(count)) thread_list = [MyThread() for i in range(2)]
for t in thread_list:
t.start()
for t in thread_list:
t.join() logger.debug('final count: {}'.format(count)) if __name__ == '__main__':
main()
修改run函数代码的不同输出:
def run(self):
global count for i in range(100):
count += 1
logger.debug('{} finished, count is {}'.format(self.name, count))
# 在切换线程之前,某一线程已经完成,两个线程顺序完成,结果几乎不会有误
# 2017-08-20 12:19:30,857 - MainThread - DEBUG - initial count: 0
# 2017-08-20 12:19:30,858 - Thread-1 - DEBUG - Thread-1 finished, count is 100
# 2017-08-20 12:19:30,858 - Thread-2 - DEBUG - Thread-2 finished, count is 200
# 2017-08-20 12:19:30,858 - MainThread - DEBUG - final count: 200 time.sleep(0.001)
for i in range(100):
count += 1
logger.debug('{} finished, count is {}'.format(self.name, count))
# 开头sleep导致两个线程几乎同时启动,结果可能有误
# 2017-08-20 12:24:59,046 - MainThread - DEBUG - initial count: 0
# 2017-08-20 12:24:59,048 - Thread-2 - DEBUG - Thread-2 finished, count is 124
# 2017-08-20 12:24:59,048 - Thread-1 - DEBUG - Thread-1 finished, count is 153
# 2017-08-20 12:24:59,048 - MainThread - DEBUG - final count: 153 for i in range(10000):
count += 1
logger.debug('{} finished, count is {}'.format(self.name, count))
# bytecodes足够导致两个线程交替运行,结果大概率有误
# 2017-08-20 12:20:17,719 - MainThread - DEBUG - initial count: 0
# 2017-08-20 12:20:17,723 - Thread-1 - DEBUG - Thread-1 finished, count is 12438
# 2017-08-20 12:20:17,723 - Thread-2 - DEBUG - Thread-2 finished, count is 12616
# 2017-08-20 12:20:17,723 - MainThread - DEBUG - final count: 12616 with lock:
for i in range(10000):
count += 1
logger.debug('{} finished, count is {}'.format(self.name, count))
# lock直到某一线程完成,结果正确
# 2017-08-20 12:20:37,630 - MainThread - DEBUG - initial count: 0
# 2017-08-20 12:20:37,631 - Thread-1 - DEBUG - Thread-1 finished, count is 10000
# 2017-08-20 12:20:37,632 - Thread-2 - DEBUG - Thread-2 finished, count is 20000
# 2017-08-20 12:20:37,634 - MainThread - DEBUG - final count: 20000 for i in range(10000):
with lock:
count += 1
logger.debug('{} finished, count is {}'.format(self.name, count))
# 两个线程交替lock,结果正确
# 2017-08-20 12:21:03,921 - MainThread - DEBUG - initial count: 0
# 2017-08-20 12:21:03,973 - Thread-1 - DEBUG - Thread-1 finished, count is 19979
# 2017-08-20 12:21:03,973 - Thread-2 - DEBUG - Thread-2 finished, count is 20000
# 2017-08-20 12:21:03,973 - MainThread - DEBUG - final count: 20000
3.观察block
def run(self):
global count all = range(10000) #确保每个线程 +1 的次数
while all != []:
if not lock.acquire(False): #假设没有参数会导致block,则马上返回false当不block;否则返回true且acquire
logger.debug('{} wait...{}'.format(self.name, len(all)))
else:
try:
count += 1
all.pop()
except Exception as err:
logger.debug('{} err, count is {}'.format(self.name, count))
finally:
# logger.debug('{} release {} {}'.format(self.name, count, len(all))) #导致两个线程顺序执行???
lock.release()
logger.debug('{} finished, count is {}'.format(self.name, count))
输出:
2017-08-20 12:32:55,204 - MainThread - DEBUG - initial count: 0
2017-08-20 12:32:55,210 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,210 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,210 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,210 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,210 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,210 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,210 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,211 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,213 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,213 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,213 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,213 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,214 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,214 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,214 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,214 - Thread-1 - DEBUG - Thread-1 wait...9925
2017-08-20 12:32:55,216 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,216 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,216 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,216 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,216 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,217 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,217 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,217 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,217 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,217 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,217 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,217 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,217 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,217 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,219 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,219 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,219 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,219 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,219 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,220 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,220 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,220 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,220 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,220 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,220 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,220 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,220 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,220 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,220 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,221 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,221 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,221 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,221 - Thread-2 - DEBUG - Thread-2 wait...6036
2017-08-20 12:32:55,226 - Thread-1 - DEBUG - Thread-1 finished, count is 13964
2017-08-20 12:32:55,236 - Thread-2 - DEBUG - Thread-2 finished, count is 20000
2017-08-20 12:32:55,236 - MainThread - DEBUG - final count: 20000
4.threading.RLock() 的应用场景
lock = threading.Lock() def get_first_part():
with lock: # any thread that attempts to acquire the lock will block, even if the same thread is already holding the lock.
... fetch data for first part from shared object
return data def get_second_part():
with lock:
... fetch data for second part from shared object
return data def get_both_parts():
with lock: # other thread may modify the resource between the two calls
first = get_first_part()
# between the two calls
second = get_second_part()
return first, second # While simple locks will block if the same thread attempts to acquire the same lock twice,
# a re-entrant lock only blocks if another thread currently holds the lock.
rlock = threading.RLock()
python之多线程 threading.Lock() 和 threading.RLock()的更多相关文章
- python的threading的使用(join方法,多线程,锁threading.Lock和threading.Condition
一.开启多线程方法一 import threading,time def write1(): for i in range(1,5): print('1') time.sleep(1) def wri ...
- python爬虫——多线程+协程(threading+gevent)
上一篇博客中我介绍了如何将爬虫改造为多进程爬虫,但是这种方法对爬虫效率的提升不是非常明显,而且占用电脑cpu较高,不是非常适用于爬虫.这篇博客中,我将介绍在爬虫中广泛运用的多线程+协程的解决方案,亲测 ...
- 基于Python的多线程模块Threading小结
步入正题前,先准备下基本知识,线程与进程的概念. 相信作为一个测试人员,如果从理论概念上来说其两者的概念或者区别,估计只会一脸蒙蔽,这里就举个例子来说明下其中的相关概念. 平安夜刚过,你是吃到了苹果还 ...
- Python:多线程编程
1.IO编程 IO(input/output).凡是用到数据交换的地方,都会涉及io编程,例如磁盘,网络的数据传输.在IO编程中,stream(流)是一种重要的概念,分为输入流(input strea ...
- 孤荷凌寒自学python第三十九天python 的线程锁Lock
孤荷凌寒自学python第三十九天python的线程锁Lock (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 当多个线程同时操作一个文件等需要同时操作某一对象的情况发生时,很有可能发生冲突, ...
- Day12- Python基础12 线程、GIL、Lock锁、RLock锁、Semaphore锁、同步条件event
http://www.cnblogs.com/yuanchenqi/articles/6248025.html 博客地址 本节内容: 1:进程和线程的说明 2:线程的两种调用方式 3:threadi ...
- python中多线程相关
基础知识 进程:进程就是一个程序在一个数据集上的一次动态执行过程 数据集:程序执行过程中需要的资源 进程控制块:完成状态保存的单元 线程:线程是寄托在进程之上,为了提高系统的并发性 线程是进程的实体 ...
- python网络-多线程(22)
一.什么是线程 线程(英语:thread)是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.同一进程中的多条线程将共享该进程中的全部系统资源,一个进程可以有很多线程,每 ...
- python 使用多线程进行并发编程/互斥锁的使用
import threading import time """ python的thread模块是比较底层的模块,python的threading模块是对thread做了 ...
随机推荐
- Python os.removedirs() 和shutil.rmtree() 用于删除文件夹
概述 os.removedirs() 方法用于递归删除目录.像rmdir(), 如果子文件夹成功删除, removedirs()才尝试它们的父文件夹,直到抛出一个error(它基本上被忽略,因为它一般 ...
- [SCOI2014]方伯伯的OJ
看到这道题的第一想法就是要用FHQ treap 过了这道题...于是至今尚未成功(华丽的 T 掉了 (╯‵□′)╯︵┻━┻ ).于是附个地址. 然后水一波博客. 题意简介 emmmm...方伯伯脑抽做 ...
- <杂记>该换个背景图了
..当然我刚开始也是懵逼的,我有发现这里可以写css,但是还是缺个图片地址,想了想,这不是还有个相册功能吗. 那应该就是把自己要换的图片上传到相册吧. 右击图片,选择检查元素找到图片的src 如:ht ...
- web页面加载、解析、渲染过程
仅做学习参考,侵权删 原文链接:风吹De麦浪 https://www.cnblogs.com/CandyManPing/p/6635008.html 一.浏览器 浏览器的主要功能是将用户选择的we ...
- 【转】Hibernate 配置
转自:http://www.blogjava.net/19851985lili/articles/108128.html 由于Hibernate是为了能在各种不同环境下工作而设计的, 因此存在着大量的 ...
- 【原创】Linux基础之Shell脚本常用命令
#!/bin/sh 1 取脚本参数 $# 参数个数$0 当前脚本名$1 第1个参数$n 第n个参数$* 所有参数$@ 所有参数$? 上个命令的状态$$ 当前pid 2 日期 $ dateWed Mar ...
- python之hashlib
简介: 用于加密相关的操作,代替了md5模块和sha模块,主要提供SHA1,SHA224,SHA256,SHA384,SHA512,MD5算法.在python3中已经废弃了md5和sha模块,简单说明 ...
- The word 'localhost' is not correctly spelled 这个问题怎么解决
The word 'localhost' is not correctly spelled 这个问题怎么解决 有时工程中有下划线并提示 The word is not correctly spelle ...
- python之+=与+(转载)
先看一个简单的例子 从程序分析,进行直接+操作后,python会重新生成一个对象,而进行+=操作并不改变原来的对象,是在原来对象的基础上进行操作,所以+=也称为就地加 除此之外+和+=还有不同: 从程 ...
- vue三大框架
vue 前端三大新框架: Angular.js------Google研发 缺点: 学习成本高.最早研发 严谨 React.js facebook.com (脸书)自主研发 开源 j ...