资讯专栏INFORMATION COLUMN

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

lowett / 3631人阅读

摘要:从调用方法启动线程,到方法执行完毕或遇到未处理异常而中断这段时间内,线程是激活的调用将会使主调线程堵塞,直到被调用线程运行结束或超时。对象实现了简单的线程通信机制,它提供了设置信号,清除信号,等待等用于实现线程间的通信。

线程和进程

</>复制代码

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

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

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

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

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

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

threading.Thread

</>复制代码

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

先来看看通过继承threading.Thread类来创建线程的例子:

</>复制代码

  1. import threading
  2. import time
  3. class MyThread(threading.Thread):
  4. def __init__(self, arg):
  5. # super(MyThread, self).__init__() # 新式类继承原有方法写法
  6. threading.Thread.__init__(self)
  7. self.arg = arg
  8. def run(self):
  9. time.sleep(2)
  10. print(self.arg)
  11. for i in range(10):
  12. thread = MyThread(i)
  13. print(thread.name)
  14. thread.start()

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

</>复制代码

  1. import threading
  2. import time
  3. def process(arg):
  4. time.sleep(2)
  5. print(arg)
  6. for i in range(10):
  7. t = threading.Thread(target=process, args=(i,))
  8. print(t.name)
  9. t.start()
Thread类还定义了以下常用方法与属性:

</>复制代码

  1. Thread.getName() 获取线程名称
    Thread.setName() 设置线程名称
    Thread.name 线程名称

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

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

  3. Thread.is_alive()
    Thread.isAlive()

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

Python GIL(Global Interpreter Lock)

</>复制代码

  1. 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。

线程锁的使用:

</>复制代码

  1. # 锁:GIL 全局解释器 它是为了保证线程在运行过程中不被抢占
  2. number = 0
  3. lock = threading.RLock() # 创建锁
  4. def run(num):
  5. lock.acquire() # 加锁
  6. global number
  7. number += 1
  8. print(number)
  9. time.sleep(2)
  10. lock.release() # 释放锁
  11. for i in range(10):
  12. t = threading.Thread(target=run, args=(i, ))
  13. t.start()
Join & Daemon

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

</>复制代码

  1. class MyThread1(threading.Thread):
  2. def __init__(self):
  3. threading.Thread.__init__(self)
  4. def run(self):
  5. print("thread start")
  6. time.sleep(3)
  7. print("thread end")
  8. print("main start")
  9. thread1 = MyThread1()
  10. # thread1.setDaemon(True) # 设置子线程是否跟随主线程一起结束
  11. thread1.start()
  12. time.sleep(1)
  13. print("satrt join")
  14. # thread1.join() # 使主线程阻塞,直至子线程运行完毕再继续主线程
  15. print("end join")

</>复制代码

  1. def run(n):
  2. print("[%s]------running----
  3. " % n)
  4. time.sleep(2)
  5. print("--done--")
  6. def main():
  7. for i in range(5):
  8. t = threading.Thread(target=run, args=[i,])
  9. t.start()
  10. # t.join()
  11. print("starting thread", t.getName())
  12. m = threading.Thread(target=main,args=[])
  13. # m.setDaemon(True) # 将主线程设置为Daemon线程,它退出时,其它子线程会同时退出,不管是否执行完任务
  14. m.start()
  15. # m.join() # 使主线程阻塞,直至子线程运行完毕再继续主线程
  16. print("---main thread done----")
线程锁(互斥锁Mutex)

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

</>复制代码

  1. num = 100 # 设定一个共享变量
  2. def subNum():
  3. global num # 在每个线程中都获取这个全局变量
  4. print("--get num:", num)
  5. time.sleep(2)
  6. num -= 1 # 对此公共变量进行-1操作
  7. thread_list = []
  8. for i in range(100):
  9. t = threading.Thread(target=subNum)
  10. t.start()
  11. thread_list.append(t)
  12. for t in thread_list: # 等待所有线程执行完毕
  13. t.join()
  14. print("final num:", num)

</>复制代码

  1. # 加锁版本
  2. def subNum():
  3. global num # 在每个线程中都获取这个全局变量
  4. print("--get num:", num)
  5. time.sleep(1)
  6. lock.acquire() # 修改数据前加锁
  7. num -= 1 # 对此公共变量进行-1操作
  8. lock.release() # 修改后释放
  9. num = 100 # 设定一个共享变量
  10. thread_list = []
  11. lock = threading.Lock() # 生成全局锁
  12. for i in range(100):
  13. t = threading.Thread(target=subNum)
  14. t.start()
  15. thread_list.append(t)
  16. for t in thread_list: # 等待所有线程执行完毕
  17. t.join()
  18. print("final num:", num)
Rlock与Lock的区别:

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

Events

</>复制代码

  1. Python提供了Event对象用于线程间通信,它是由线程设置的信号标志,如果信号标志位真,则其他线程等待直到信号接触。

    Event对象实现了简单的线程通信机制,它提供了设置信号,清除信号,等待等用于实现线程间的通信。

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

  3. 1 设置信号
    event.set()

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

  5. 2 清除信号
    event.clear()

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

  7. 3 等待
    event.wait()

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

</>复制代码

  1. def do(event):
  2. print("start")
  3. event.wait()
  4. print("execute")
  5. event_obj = threading.Event()
  6. for i in range(10):
  7. t = threading.Thread(target=do, args=(event_obj,))
  8. t.start()
  9. event_obj.clear()
  10. inp = input("输入内容:")
  11. if inp == "true":
  12. event_obj.set()

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41637.html

相关文章

  • python模块之threading

    摘要:的类行为是的类行为的子集,目前尚不支持优先级线程组,线程无法销毁停止暂停恢复或中断。表示继承创建该线程的当前线程的属性。重入锁,同步原语的一种,可由同一线程多次获取已持有的锁。 threading在低级的_thread模块上构建了更高级的线程接口。 threading模块基于Java线程模型设计。不过Java中锁和条件变量是每个对象的基本行为,在python中却是单独的对象。pytho...

    nanchen2251 评论0 收藏0
  • python并发4:使用thread处理并发

    摘要:如果某线程并未使用很多操作,它会在自己的时间片内一直占用处理器和。在中使用线程在和等大多数类系统上运行时,支持多线程编程。守护线程另一个避免使用模块的原因是,它不支持守护线程。 这一篇是Python并发的第四篇,主要介绍进程和线程的定义,Python线程和全局解释器锁以及Python如何使用thread模块处理并发 引言&动机 考虑一下这个场景,我们有10000条数据需要处理,处理每条...

    joywek 评论0 收藏0
  • # Python 线程

    摘要:多线程和锁作者博客进程和线程进程是执行中的计算机程序。线程包括开始执行顺序和结束三部分。的多进程相关模块模块是高级别的多线程模块。线程锁当多线程争夺锁时,允许第一个获得锁的线程进入临街区,并执行代码。 Python 多线程和锁 作者博客:http://zzir.cn/ 进程和线程 进程是执行中的计算机程序。每个进程都拥有自己的地址空间、内存、数据栈及其它的辅助数据。操作系统管理着所有的...

    cpupro 评论0 收藏0
  • python并发编程思考

    摘要:我们以请求网络服务为例,来实际测试一下加入多线程之后的效果。所以,执行密集型操作时,多线程是有用的,对于密集型操作,则每次只能使用一个线程。说到这里,对于密集型,可以使用多线程或者多进程来提高效率。 为了提高系统密集型运算的效率,我们常常会使用到多个进程或者是多个线程,python中的Threading包实现了线程,multiprocessing 包则实现了多进程。而在3.2版本的py...

    sshe 评论0 收藏0
  • 谈谈Python协程技术演进

    摘要:事件循环是异步编程的底层基石。对事件集合进行轮询,调用回调函数等一轮事件循环结束,循环往复。协程直接利用代码的执行位置来表示状态,而回调则是维护了一堆数据结构来处理状态。时代的协程技术主要是,另一个比较小众。 Coding Crush Python开发工程师 主要负责岂安科技业务风险情报系统redq。 引言 1.1. 存储器山 存储器山是 Randal Bryant 在《深入...

    zhiwei 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<