详解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兔子毒药问题实例分析
Mar 05 Python
Using Django with GAE Python 后台抓取多个网站的页面全文
Feb 17 Python
Python中关于Sequence切片的下标问题详解
Jun 15 Python
Python 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)
Mar 19 Python
Python对List中的元素排序的方法
Apr 01 Python
python 3.6.4 安装配置方法图文教程
Sep 18 Python
Python 图像对比度增强的几种方法(小结)
Sep 25 Python
windows下的pycharm安装及其设置中文菜单
Apr 23 Python
Python并发concurrent.futures和asyncio实例
May 04 Python
Django项目创建及管理实现流程详解
Oct 13 Python
python链表类中获取元素实例方法
Feb 23 Python
Python+uiautomator2实现自动刷抖音视频功能
Apr 29 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 多维数组排序实现代码
2009/08/05 PHP
LotusPhp笔记之:基于ObjectUtil组件的使用分析
2013/05/06 PHP
php实现的百度搜索某地天气的小偷代码
2014/04/23 PHP
使用ob系列函数实现PHP网站页面静态化
2014/08/13 PHP
php返回当前日期或者指定日期是周几
2015/05/21 PHP
phpcms配置列表页以及获得文章发布时间
2017/07/04 PHP
PHP常量及变量区别原理详解
2020/08/14 PHP
基于Jquery的实现回车键Enter切换焦点
2010/09/14 Javascript
jQuery javaScript捕获回车事件(示例代码)
2013/11/07 Javascript
关于jquery中全局函数each使用介绍
2013/12/10 Javascript
jquery+php实现滚动的数字特效
2015/11/29 Javascript
详解angular中通过$location获取路径(参数)的写法
2017/03/21 Javascript
Three.js基础学习之场景对象
2017/09/27 Javascript
JavaScript门面模式详解
2017/10/19 Javascript
Angular5中调用第三方js插件的方法
2018/02/26 Javascript
js实现ATM机存取款功能
2020/10/27 Javascript
js核心基础之构造函数constructor用法实例分析
2019/05/11 Javascript
[48:20]OpTic vs Serenity 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
Python使用scrapy抓取网站sitemap信息的方法
2015/04/08 Python
python九九乘法表的实例
2017/09/26 Python
Python中使用支持向量机SVM实践
2017/12/27 Python
致Python初学者 Anaconda入门使用指南完整版
2018/04/05 Python
使用Python横向合并excel文件的实例
2018/12/11 Python
深入浅析python 协程与go协程的区别
2019/05/09 Python
如何理解Python中包的引入
2020/05/29 Python
python对 MySQL 数据库进行增删改查的脚本
2020/10/22 Python
python 如何停止一个死循环的线程
2020/11/24 Python
有原因的手表:Flex Watches
2019/03/23 全球购物
信息技术培训感言
2014/03/06 职场文书
《搭石》教学反思
2014/04/07 职场文书
整改报告怎么写
2014/11/06 职场文书
本科毕业论文答辩稿
2015/06/23 职场文书
golang 在windows中设置环境变量的操作
2021/04/29 Golang
Python制作一个随机抽奖小工具的实现
2021/07/07 Python
Python制作动态字符画的源码
2021/08/04 Python
苹果发布了MagSafe固件更新,可以不外接电源实现最高7.5W充电
2022/04/21 数码科技