您好,欢迎访问一九零五行业门户网

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

这篇文章主要介绍了详解python多线程、锁、event事件机制的简单使用,现在分享给大家,也给大家做个参考。一起过来看看吧
线程和进程
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多线程中阻塞(join)与锁(lock)使用误区解析
python多线程之事件event的使用详解
以上就是详解python多线程、锁、event事件机制的简单使用的详细内容。
其它类似信息

推荐信息