详解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 相关文章推荐
py2exe 编译ico图标的代码
Mar 08 Python
python快速排序代码实例
Nov 21 Python
跟老齐学Python之有点简约的元组
Sep 24 Python
在python中使用正则表达式查找可嵌套字符串组
Oct 24 Python
使用Python进行QQ批量登录的实例代码
Jun 11 Python
python定向爬虫校园论坛帖子信息
Jul 23 Python
python的pip安装以及使用教程
Sep 18 Python
django的csrf实现过程详解
Jul 26 Python
使用IDLE的Python shell窗口实例详解
Nov 19 Python
Python requests设置代理的方法步骤
Feb 23 Python
Python解释器以及PyCharm的安装教程图文详解
Feb 26 Python
keras的load_model实现加载含有参数的自定义模型
Jun 22 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
Yii2框架实现注册和登录教程
2016/09/30 PHP
php封装的page分页类完整实例
2016/10/18 PHP
php die()与exit()的区别实例详解
2016/12/03 PHP
WEB 浏览器兼容 推荐收藏
2010/05/14 Javascript
JQuery里选择超链接的实现代码
2011/05/22 Javascript
修改好的jquery滚动字幕效果实现代码
2011/06/22 Javascript
自己写了一个展开和收起的多更能型的js效果
2013/03/05 Javascript
JS实现时间格式化的方式汇总
2013/10/16 Javascript
jquery 取子节点及当前节点属性值
2014/07/25 Javascript
Windows系统下使用Sublime搭建nodejs环境
2015/04/13 NodeJs
js实现点击切换TAB标签实例
2015/08/21 Javascript
jQuery实现简易的天天爱消除小游戏
2015/10/16 Javascript
AngularJS利用Controller完成URL跳转
2016/08/09 Javascript
微信小程序页面滑动屏幕加载数据效果
2020/11/16 Javascript
AngularJS下$http服务Post方法传递json参数的实例
2018/03/29 Javascript
实例详解Vue项目使用eslint + prettier规范代码风格
2018/08/20 Javascript
使用jquery模拟a标签的click事件无法实现跳转的解决
2018/12/04 jQuery
实例讲解vue源码架构
2019/01/24 Javascript
layui checkbox默认选中,获取选中值,清空所有选中项的例子
2019/09/02 Javascript
Python中用sleep()方法操作时间的教程
2015/05/22 Python
更改Ubuntu默认python版本的两种方法python-> Anaconda
2016/12/18 Python
python实现单向链表详解
2018/02/08 Python
Python图像处理实现两幅图像合成一幅图像的方法【测试可用】
2019/01/04 Python
python中单下划线(_)和双下划线(__)的特殊用法
2019/08/29 Python
如何基于Python爬虫爬取美团酒店信息
2020/11/03 Python
Python基于template实现字符串替换
2020/11/27 Python
Python读取图像并显示灰度图的实现
2020/12/01 Python
世界第一曲奇连锁店:Mrs. Fields Cookies
2017/02/04 全球购物
美国医疗用品、医疗设备和家庭保健用品商店:Medical Supply Depot
2018/07/08 全球购物
世界排名第一的运动鞋市场:Flight Club
2020/01/03 全球购物
香奈儿美国官网:CHANEL美国
2020/05/20 全球购物
新电JAVA笔试题目
2014/08/31 面试题
健康状况证明模板
2014/10/23 职场文书
2014年幼儿园老师工作总结
2014/12/05 职场文书
党员反腐倡廉学习心得体会
2015/08/15 职场文书
2016年禁毒宣传活动总结
2016/04/05 职场文书