gpt4 book ai didi

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

转载 作者:qq735679552 更新时间:2022-09-28 22:32:09 28 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章详解python多线程、锁、event事件机制的简单使用由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

线程和进程 。

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

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

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

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

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

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

threading.Thread 。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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()

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

?
1
2
3
4
5
6
7
8
9
10
11
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.

线程锁的使用:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 锁: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() 方法调用之前设置,如果不设置为守护线程,程序会被无限挂起.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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' )
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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个线程同时要修改同一份数据,会出现什么状况?

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 加锁版本
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新号标志位真 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 ()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.

原文链接:https://segmentfault.com/a/1190000014619654 。

最后此篇关于详解python多线程、锁、event事件机制的简单使用的文章就讲到这里了,如果你想了解更多关于详解python多线程、锁、event事件机制的简单使用的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com