详解python多线程、锁、event事件机制的简单使用


Posted in Python onApril 27, 2018

线程和进程

1、线程共享创建它的进程的地址空间,进程有自己的地址空间

2、线程可以访问进程所有的数据,线程可以相互访问

3、线程之间的数据是独立的

4、子进程复制线程的数据

5、子进程启动后是独立的 ,父进程只能杀掉子进程,而不能进行数据交换

6、修改线程中的数据,都是会影响其他的线程,而对于进程的更改,不会影响子进程

threading.Thread

Thread 是threading模块中最重要的类之一,可以使用它来创建线程。有两种方式来创建线程:一种是通过继承Thread类,重写它的run方法;另一种是创建一个threading.Thread对象,在它的初始化函数(__init__)中将可调用对象作为参数传入。
先来看看通过继承threading.Thread类来创建线程的例子:

import threading
import time

class MyThread(threading.Thread):
 def __init__(self, arg):
  # super(MyThread, self).__init__() # 新式类继承原有方法写法
  threading.Thread.__init__(self)
  self.arg = arg

 def run(self):
  time.sleep(2)
  print(self.arg)

for i in range(10):
 thread = MyThread(i)
 print(thread.name)
 thread.start()

另外一种创建线程的方法:

import threading
import time

def process(arg):
 time.sleep(2)
 print(arg)

for i in range(10):
 t = threading.Thread(target=process, args=(i,))
 print(t.name)
 t.start()

Thread类还定义了以下常用方法与属性:

Thread.getName() 获取线程名称

Thread.setName() 设置线程名称

Thread.name 线程名称

Thread.ident 获取线程的标识符。线程标识符是一个非零整数,只有在调用了start()方法之后该属性才有效,否则它只返回None

判断线程是否是激活的(alive)。从调用start()方法启动线程,到run()方法执行完毕或遇到未处理异常而中断 这段时间内,线程是激活的

Thread.is_alive()
Thread.isAlive()

Thread.join([timeout]) 调用Thread.join将会使主调线程堵塞,直到被调用线程运行结束或超时。参数timeout是一个数值类型,表示超时时间,如果未提供该参数,那么主调线程将一直堵塞到被调线程结束

Python GIL(Global Interpreter Lock)

GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL。

线程锁的使用:

# 锁:GIL 全局解释器 它是为了保证线程在运行过程中不被抢占
number = 0
lock = threading.RLock() # 创建锁


def run(num):
 lock.acquire() # 加锁
 global number
 number += 1
 print(number)
 time.sleep(2)
 lock.release() # 释放锁

for i in range(10):
 t = threading.Thread(target=run, args=(i, ))
 t.start()

Join & Daemon

主线程A中,创建了子线程B,并且在主线程A中调用了B.setDaemon(),这个的意思是,把主线程A设置为守护线程,这时候,要是主线程A执行结束了,就不管子线程B是否完成,一并和主线程A退出.这就是setDaemon方法的含义,这基本和join是相反的。此外,还有个要特别注意的:必须在start() 方法调用之前设置,如果不设置为守护线程,程序会被无限挂起。

class MyThread1(threading.Thread):
 def __init__(self):
  threading.Thread.__init__(self)

 def run(self):
  print("thread start")
  time.sleep(3)
  print('thread end')

print('main start')
thread1 = MyThread1()
# thread1.setDaemon(True)  # 设置子线程是否跟随主线程一起结束
thread1.start()
time.sleep(1)
print('satrt join')
# thread1.join() # 使主线程阻塞,直至子线程运行完毕再继续主线程
print('end join')
def run(n):
 print('[%s]------running----\n' % n)
 time.sleep(2)
 print('--done--')


def main():
 for i in range(5):
  t = threading.Thread(target=run, args=[i,])
  t.start()
  # t.join()
  print('starting thread', t.getName())


m = threading.Thread(target=main,args=[])
# m.setDaemon(True) # 将主线程设置为Daemon线程,它退出时,其它子线程会同时退出,不管是否执行完任务
m.start()
# m.join() # 使主线程阻塞,直至子线程运行完毕再继续主线程
print("---main thread done----")

线程锁(互斥锁Mutex)

一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况?

num = 100 # 设定一个共享变量
def subNum():
 global num # 在每个线程中都获取这个全局变量
 print('--get num:', num)
 time.sleep(2)
 num -= 1 # 对此公共变量进行-1操作
thread_list = []
for i in range(100):
 t = threading.Thread(target=subNum)
 t.start()
 thread_list.append(t)
for t in thread_list: # 等待所有线程执行完毕
 t.join()
print('final num:', num)
# 加锁版本
def subNum():
 global num # 在每个线程中都获取这个全局变量
 print('--get num:', num)
 time.sleep(1)
 lock.acquire() # 修改数据前加锁
 num -= 1 # 对此公共变量进行-1操作
 lock.release() # 修改后释放

num = 100 # 设定一个共享变量
thread_list = []
lock = threading.Lock() # 生成全局锁
for i in range(100):
 t = threading.Thread(target=subNum)
 t.start()
 thread_list.append(t)
for t in thread_list: # 等待所有线程执行完毕
 t.join()
print('final num:', num)

Rlock与Lock的区别:

RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。否则会出现死循环,程序不知道解哪一把锁。注意:如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的锁

Events

Python提供了Event对象用于线程间通信,它是由线程设置的信号标志,如果信号标志位真,则其他线程等待直到信号接触。
Event对象实现了简单的线程通信机制,它提供了设置信号,清除信号,等待等用于实现线程间的通信。

event = threading.Event() 创建一个event

1 设置信号
event.set()

使用Event的set()方法可以设置Event对象内部的信号标志为真。Event对象提供了isSet()方法来判断其内部信号标志的状态。
当使用event对象的set()方法后,isSet()方法返回真

2 清除信号
event.clear()

使用Event对象的clear()方法可以清除Event对象内部的信号标志,即将其设为假,当使用Event的clear方法后,isSet()方法返回假

3 等待
event.wait()

Event对象wait的方法只有在内部信号为真的时候才会很快的执行并完成返回。当Event对象的内部信号标志位假时,
则wait方法一直等待到其为真时才返回。也就是说必须set新号标志位真

def do(event):
 print('start')
 event.wait()
 print('execute')

event_obj = threading.Event()
for i in range(10):
 t = threading.Thread(target=do, args=(event_obj,))
 t.start()

event_obj.clear()
inp = input('输入内容:')
if inp == 'true':
 event_obj.set()

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

Python 相关文章推荐
在Python中处理XML的教程
Apr 29 Python
python实现协同过滤推荐算法完整代码示例
Dec 15 Python
Python利用splinter实现浏览器自动化操作方法
May 11 Python
python实现梯度下降算法
Mar 24 Python
在unittest中使用 logging 模块记录测试数据的方法
Nov 30 Python
对python中xlsx,csv以及json文件的相互转化方法详解
Dec 25 Python
Python综合应用名片管理系统案例详解
Jan 03 Python
详解Python 循环嵌套
Jul 09 Python
python删除文件、清空目录的实现方法
Sep 23 Python
python 日志模块logging的使用场景及示例
Jan 04 Python
python用分数表示矩阵的方法实例
Jan 11 Python
python套接字socket通信
Apr 01 Python
Python Requests模拟登录实现图书馆座位自动预约
Apr 27 #Python
Python多线程中阻塞(join)与锁(Lock)使用误区解析
Apr 27 #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
You might like
php 生成WML页面方法详解
2009/08/09 PHP
PHP5.3的垃圾回收机制(动态存储分配方案)深入理解
2012/12/10 PHP
Opcache导致php-fpm崩溃nginx返回502
2015/03/02 PHP
PHP制作登录异常ip检测功能的实例代码
2016/11/16 PHP
PHP实现将多个文件压缩成zip格式并下载到本地的方法示例
2018/05/23 PHP
延时重复执行函数 lLoopRun.js
2007/05/08 Javascript
Knockout visible绑定使用方法
2013/11/15 Javascript
浅析JavaScript中的typeof运算符
2013/11/30 Javascript
javascript性能优化之DOM交互操作实例分析
2015/12/12 Javascript
jQuery插件实现文字无缝向上滚动效果代码
2016/02/25 Javascript
JavaScript 浏览器兼容性总结及常用浏览器兼容性分析
2016/03/30 Javascript
JavaScript图像延迟加载库Echo.js
2016/04/05 Javascript
jQuery中的select操作详解
2016/11/29 Javascript
jQuery设置和获取select、checkbox、radio的选中值方法
2017/01/01 Javascript
jQuery实现表格元素动态创建功能
2017/01/09 Javascript
JS实现自动轮播图效果(自适应屏幕宽度+手机触屏滑动)
2017/06/19 Javascript
vue用addRoutes实现动态路由的示例
2017/09/15 Javascript
python基础教程之面向对象的一些概念
2014/08/29 Python
python中元类用法实例
2014/10/10 Python
pygame学习笔记(5):游戏精灵
2015/04/15 Python
Python实例一个类背后发生了什么
2016/02/09 Python
python 3调用百度OCR API实现剪贴板文字识别
2018/09/04 Python
ubuntu16.04制作vim和python3的开发环境
2018/09/23 Python
python自动分箱,计算woe,iv的实例代码
2019/11/22 Python
通过实例解析Python RPC实现原理及方法
2020/07/07 Python
html5实现完美兼容各大浏览器的播放器
2014/12/26 HTML / CSS
英国的领先快速时尚零售商:In The Style
2019/03/25 全球购物
飞利浦西班牙官方网站:Philips西班牙
2020/02/17 全球购物
经济管理专业毕业生推荐信
2013/11/11 职场文书
运动会入场解说词
2014/02/07 职场文书
安全承诺书格式
2014/05/21 职场文书
演讲稿的格式及范文
2014/08/22 职场文书
2015年高校就业工作总结
2015/05/04 职场文书
Html分层的box-shadow效果的示例代码
2021/03/30 HTML / CSS
详解Html5项目适配系统深色模式方案总结
2021/04/14 HTML / CSS
MySQL优化常用的19种有效方法(推荐!)
2022/03/17 MySQL