赞
踩
目 录
3.2.2 创建 Thread 的实例,传递给它一个可调用的类实例
Python 多线程编程目录
Python 多线程编程-01-threading 模块初识
Python 多线程编程-02-threading 模块-锁的使用
Python 多线程编程-03-threading 模块 - Condition
Python 多线程编程-04-threading 模块 - Event
Python 多线程编程-05-threading 模块 - Semaphore 和 BoundedSemaphore
Python 多线程编程-06-threading 模块 - Timer
Python 多线程编程-07-threading 模块 - Barrie
进程是一个执行中程序,每一个进程都有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据。操作系统管理其上面的所有进程,并为这些进程合理的安排时间。一个进程也可以通过 fork 或者 spawn 新的进程来执行其他任务。进程之间通过 IPC 进行通信。
进程的相关知识,可以看我的这个专栏:操作系统_一分耕耘一分收获-CSDN博客
线程和进程类似,但是它是在进程下工作的,一个进程可能拥有多个线程,彼此之间享有共同的上下文或者说数据空间。可以把它看成是迷你进程。线程之间的信息通信和数据共享更加容易,但是因为两个或者多个线程访问同一片数据空间,不同的访问顺序会导致不同的结果,这种情况称之为竞态条件(race condition)。绝大多数线程库都有同步原语来解决这个问题,一般是通过线程管理器来控制执行和访问。
一个线程包括开始、执行顺序和结束三部分。它有一个指令指针,用于记录当前运行的上下文。它可以被抢占资源(中断),可以临时挂起(睡眠),这种做饭也叫做让步。在多线程之间,不类似多进程,OS 会尽可能公平地给与时间片,线程如果没有专门进行为多线程的情况进行修改,那么线程在执行一些程序时候会保持阻塞状态,CPU的时间分配会被这些贪婪的程序多用。
Python 代码的执行是由 Python 虚拟机(又叫解释器主循环)进行控制的。Python 的设计思路是,在主循环中同时只能有一个控制线程在执行,就像单核 CPU 系统中的多进程一样,只是在宏观上看起来多进程,在微观上其实还得单个单个的运行。同理,尽管 Python 解释器中可以有多个线程在运行,但是微观时间上,只有一个线程在被真正执行。
而对 Python 虚拟机的访问,就是由全程解释器锁(Global Interpreter Lock)来控制的。这个锁就是用来保证同时只能由一个线程运行的。它的方式:
a) 指定数量的字节码指令
b) 线程主动让出控制权(可以用 tims.sleep())实现
请注意,从上面的描述,我们能够得到一个关键信息:I/O 密集型的 Python 程序比计算密集型的代码能够更好地利用多线程!如果我们的程序要处理的是计算密集型的情况,请慎用多线程编程,否则频繁的切换线程可能反而会带来性能上的下降。
Python 支持多线程编程,但是还取决于它所处的 OS 是否支持。绝大多数操作系统是支持多线程的:绝大多数 Unix 平台(Linux、Solaris、Mac OS X、*BSD)等,以及 Windows 平台。此外,Python 使用兼容 POSIX 的线程,也就是众所周知的 pthread。
在 Python 2.X 时代,使用最基础的 thread 模块只需要 import thread 语句就可以了,但是进入了 Python 3.X 时代,thread 模块越来越式微,名字甚至被改为了 _thread 。
推荐大家使用 threading 模块而不是 thread 模块的原因如下:
首先,创建一个 Thread 类对象。
- import threading
- t1=threading.Thread()
可以看到它有很多方法和属性。
序号 | 属性和方法 | 描述 |
1 | 属性:daemon | 布尔标识。表示这个线程是否是守护线程。 |
2 | 属性:ident | 线程标识符 |
3 | 属性:name | 线程名 |
4 | 属性:native_id | 线程 ID,该非负数可用于在系统范围内唯一标识此特定线程。 |
5 | 方法:getName() | 返回线程名 |
6 | 方法:is_alive() | 返回布尔标识。表示这个线程是否是否存活。 |
7 | 方法:isAlive() | 驼峰式命名已经被弃用。#DeprecationWarning: isAlive() is deprecated, use is_alive() instead |
8 | 方法:isDaemon() | 返回布尔标识。表示这个线程是否是守护线程,是则 True,否则是 False。 |
9 | 方法:start() | 开始执行该线程,请注意,一个线程只能开始执行依次,反复调用这个方法会报错:threads can only be started once; |
10 | 方法:join() | 直至启动的线程终止之前一直挂起,除非给出了 timeout(秒),否则就会一直阻塞。请注意,得先 start 才能 join。cannot join thread before it is started |
11 | 方法:run() | 定义线程功能的方法,通常在子类中被应用开发者重现 |
12 | 方法:setDaemon(daemonic) | 已经弃用,应该直接设置属性 daemon,也可以在实例化中设置 |
13 | 方法:setName("abc") | 设置线程名 |
首先,创建一个 Lock 类对象。
lock_example=threading.Lock()
序号 | 属性和方法 | 描述 |
1 | 方法:acquire(blocking=True, timeout=-1) | 如果没有参数,且这个锁已经被锁定了,哪怕是被同一个线程锁定,这个线程将会阻塞,需要等待另外一个线程释放获取锁后,返回 True。 如果有参数,只有当参数是 True时候,才会阻塞。 方法的返回值反应释放获取了锁。阻塞操作是可以中断的。 |
2 | 方法:acquire_lock() | 已经废弃 |
3 | 方法:locked() | 返回一个布尔值,指示锁的状态 |
4 | 方法:locked_lock() | 已经废弃 |
5 | 方法:release() | 释放锁,允许在等待该锁的阻塞队列中的某个线程获得这个锁。这个锁当前应该是被锁定的状态,但是不能是锁定这个锁的线程再次锁定它。 |
6 | 方法:release_lock() | 已经废弃 |
详细使用请见:Python 多线程编程-02-threading 模块-锁的使用
首先,创建一个 RLock 类对象。RLock 类对象使单一线程可以(再次)获得已持有的锁(锁递归)。
序号 | 属性和方法 | 描述 |
1 | 方法:acquire (blocking=True) | 锁定锁,返回一个布尔值指示是否锁定。blocking 指示我们是否等待这个锁可用。blocking = False 且另外一个线程占据了这个锁,那么立刻返回 False。blocking = True, 且另外一个线程占据了这个锁,那么则等待这个锁释放,拿到这个锁,然后返回 True。 请注意,阻塞操作是可中断的。在所有其他情况下,该方法将立即返回True。 确切地说,如果当前线程已经持有锁,则其内部计数器简单地递增。如果没有人拿着锁,获取锁,其内部计数器初始化为1。 |
2 | 方法:release() | 释放锁,允许在等待该锁的阻塞队列中的某个线程获得这个锁。这个锁当前应该是被锁定的状态,但是必须是锁定这个锁的线程再次锁定它。 |
详细使用请见:Python 多线程编程-02-threading 模块-锁的使用
高级用法,详细见:Python 多线程编程-03-threading 模块 - Condition
高级用法,详细见:Python 多线程编程-04-threading 模块 - Event
为线程间共享的有限资源提供了一个计数器,如果没有资源时候会被阻塞。
高级用法,详细见:Python 多线程编程-05-threading 模块 - Semaphore 和 BoundedSemaphore
和 Semaphore 类似,但是不允许超过初始值。
高级用法,详细见:Python 多线程编程-05-threading 模块 - Semaphore 和 BoundedSemaphore
高级用法,详细见:Python 多线程编程-06-threading 模块 - Timer
创造一个障碍,必须达到指定数量的线程后才可以继续。
高级用法,详细见:Python 多线程编程-07-threading 模块 - Barrier
返回当前活动的Thread 对象个数。返回值和通过enumerate()返回的列表长度是相等的。
返回当前活动的Thread 对象,对应调用者的控制线程。如果调用者的控制线程不是通过threading 模块创建,一个功能受限的虚拟线程被返回。
返回当前活动的Thread 对象列表。该列表包括精灵线程、被current_thread()创建的虚拟线程对象、和主线程。它不包括终止的线程和还没有启动的线程。
返回主线程对象。在正常情况下,主线程就是Python解释器启动的线程。
返回当前线程的“线程标识符”。这是一个非0整数,没有特定含义,通常用于索引线程特定数据的字典。线程标识符可以被循环使用。
为所有线程设置一个 trace 函数。为所有从 threading 模块启动的线程设置一个 trace 函数。在每个线程的 run() 方法被调用前,函数将为每个线程被传递到 sys.settrace()。
为所有线程设置一个 profile 函数。
返回新创建线程的栈大小;或者为后续创建的线程设定栈的大小为 size。
t=threading.Thread(group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)
使用 threading.Thread 类,可以有很多方法创建线程。主要有三种:
请注意此处在创建 Thread 类时候就指定了其调用的目标函数和参数。
- import threading
- import time
- def say_time(nSeconds,name):
- print("{0} Starting In saytime:{1}".format(name,time.ctime()))
- print(" I will sleep for {0} seconds!".format(nSeconds))
- time.sleep(nSeconds)
- print("{0} Ending In saytime:{1}".format(name,time.ctime()))
-
- n_loops=list(range(5))
-
- def main():
- threads=[]
- # 创建线程,并且指定函数
- for i in n_loops:
- thread=threading.Thread(target=say_time,args=(i+1,i))
- threads.append(thread)
- # 依次启动线程
- for i in n_loops:
- threads[i].start()
-
- for i in n_loops:
- threads[i].join()
-
- print("All done! {0}".format(time.ctime()))
-
- main()
.join() 方法等待线程结束,或者在提供了超时时间的情况,达到超时时间。使用 .join() 方法要比等待锁释放的无限循环更加清晰。此处的 .join() 可有可无,因为主程序之后没有必须等待 thread 完成的工作,但是如果不使用 .join(),对于某些对顺序有严格要求的程序,可能不合适。 如果我把此处的 .join() 注释掉,程序各条语句完成的顺序就不一样了。
- import threading
- import time
- def say_time(nSeconds,name):
- print("{0} Starting In saytime:{1}".format(name,time.ctime()))
- print(" I will sleep for {0} seconds!".format(nSeconds))
- time.sleep(nSeconds)
- print("{0} Ending In saytime:{1}".format(name,time.ctime()))
-
- n_loops=list(range(5))
-
- def main():
- threads=[]
-
- for i in n_loops:
- thread=threading.Thread(target=say_time,args=(i+1,i))
- threads.append(thread)
-
- for i in n_loops:
- threads[i].start()
-
- #for i in n_loops:
- #threads[i].join()
-
- print("All done! {0}".format(time.ctime()))
-
- main()
这种写法很累赘,目前我还没有看出来有什么好处。不过,这是一种实现方式。
- import threading
- import time
- class Say_Time(object):
- def __init__(self,func,args,name=" "):
- self.name=name
- self.func=func
- self.args=args
-
- def __call__(self):
- self.func(*self.args)
-
- def say_time(nSeconds,name):
- print("{0} Starting In saytime:{1}".format(name,time.ctime()))
- print(" I will sleep for {0} seconds!".format(nSeconds))
- time.sleep(nSeconds)
- print("{0} Ending In saytime:{1}".format(name,time.ctime()))
-
- n_loops=list(range(3))
-
- def main():
- threads=[]
- print("All Starting! {0}".format(time.ctime()))
-
-
- for i in n_loops:
- print(say_time,18+i,"name"+str(i))
- thread=threading.Thread(target=Say_Time(say_time,(18+i,"name"+str(i))))
- threads.append(thread)
-
- for i in n_loops:
- threads[i].start()
-
- for i in n_loops:
- threads[i].join()
-
- print("All done! {0}".format(time.ctime()))
-
- main()
这种方法和第一种方法用得较多。
- import threading
- import time
- class MyThread(threading.Thread):
- def __init__(self,func,args,name=" "):
- threading.Thread.__init__(self)
- self.name=name
- self.func=func
- self.args=args
-
- # run() 定义线程功能的方法,通常在子类中被应用开发者重现
- def run(self):
- self.func(*self.args)
-
- def say_time(nSeconds,name):
- print("{0} Starting In saytime:{1}".format(name,time.ctime()))
- print(" I will sleep for {0} seconds!".format(nSeconds))
- time.sleep(nSeconds)
- print("{0} Ending In saytime:{1}".format(name,time.ctime()))
-
- n_loops=list(range(3))
-
- def main():
- threads=[]
- print("All Starting! {0}".format(time.ctime()))
-
-
- for i in n_loops:
- print(say_time,18+i,"name"+str(i))
- thread=MyThread(say_time,(18+i,"name"+str(i)),say_time.__name__)
- threads.append(thread)
-
- for i in n_loops:
- threads[i].start()
-
- for i in n_loops:
- threads[i].join()
-
- print("All done! {0}".format(time.ctime()))
-
- main()
'''
要是大家觉得写得还行,麻烦点个赞或者收藏吧,想个博客涨涨人气,非常感谢!
'''
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。