文章目录
更多关于python线程
线程
-
Thread
class threading.Thread(*group=None*, *target=None*, *name=None*, args=(), kwargs={}, daemon=None*)
参数 说明 group 应该为 None;为了日后扩展 ThreadGroup 类实现而保留 target 用于 run() 方法调用的可调用对象(可以是函数).默认是 None,表示不需要调用任何方法 name 是线程名称.默认情况下,由"Thread-N"格式构成一个唯一的名称,其中 N 是小的十进制数。 args 是用于调用目标函数的参数元组。默认是 () kwargs 是用于调用目标函数的关键字参数字典。默认是 {} daemon 如果是 None (默认值),线程将继承当前线程的守护模式属性,显式地设置该线程是否为守护模式 函数 说明 start() 开始线程活动。同一线程对象在一个线程中最懂只能被调用一次,他安排的对象run()方法在一个独立的控制进程中调用;如果同一线程对象中调用这个方法的次数大于一次,会抛弃RuntimeError run() 代表线程活动的方法 join(timeout=None) 等待,直到线程终结。这会阻塞调用这个方法的线程,知道被调用join()的线程终结 name 只用于识别的字符串。它没有语义。多个线程可以赋予相同的名称。 初始名称由构造函数设置 getName() 旧的name取值,直接当作特征属性使用它 setName() 旧的name设值API;可以直接做特征属性使用它 ident 这个线程的 ‘线程标识符’,如果线程尚未开始则为 None
。这是个非零整数。注意:一个线程退出而另外一个线程被创建,线程标识符会被复用。即使线程退出后,仍可得到标识符native_id 此线程的原生集成线程 ID。 这是一个非负整数,或者如果线程还未启动则为 None
。 这表示线程 ID (TID
) 已被 OS (内核) 赋值给线程。 它的值可能被用来在全系统范围内唯一地标识这个特定线程(直到线程终结,在那之后该值可能会被 OS 回收再利用)is_alive() 返回线程是否存活当 run()
方法刚开始直到run()
方法刚结束,这个方法返回 `Truedaemon 一个表示这个线程是(True)否(False)守护线程的布尔值。一定要在调用 start()
前设置好,不然会抛出RuntimeError
。初始值继承于创建线程;主线程不是守护线程,因此主线程创建的所有线程默认都是daemon
=False
isDaemon 旧的 name
取值 API;建议直接当做特征属性使用它setDaemon 旧的 name
设值 API;建议直接当做特征属性使用它import threading import time def show_mes(mes="我才刚开始"): print(mes) if __name__ == "__main__": t1 = threading.Thread(target=show_mes) t1.start() time.sleep(1) t1.join(timeout=2) print("hahhah")
-
返回当前存活的线程类对象:
threading.alive_count()
-
返回当前调试者调用的控制线程的对象:
threading.current_thread()
-
获取当前主线程:
threading.main_threading
-
获取线程默认最大回响延迟:
threading.TIMEOUT_MAX
-
获取当前线程对象的"线程标识符":
threading.get_ident()
-
获取内核分配给当前线程的原生集成线程:
threading.get_native_id
-
以列表形式返回当前所有存活的线程对象:
threading.enumerate()
import threading g_num = 0 def test1(num): print("当前正在调用的线程是{}".format(threading.current_thread())) global g_num for i in range(num): g_num += 1 def test2(num): print("当前正在调用的线程是{}".format(threading.current_thread())) global g_num for i in range(num): g_num += 1 def main(): t1 = threading.Thread(target=test1, args=(100, )) t2 = threading.Thread(target=test2, args=(100, )) print("当前存活的线程数是%d " % threading.active_count()) print("当前存活的线程是{} ".format(threading.enumerate())) t1.start() print("当前存活的线程数是%d " % threading.active_count()) print("当前存活的线程是{} ".format(threading.enumerate())) t2.start() print("当前存活的线程数是%d " % threading.active_count()) print("当前存活的线程是{} ".format(threading.enumerate())) if __name__ == "__main__": print("当前主线程是{}".format(threading.main_threading)) print("系统默认线程调用最大的延时为 %d s" % threading.TIMEOUT_MAX) main()
观察到
threading.alive_count*() == len(threading.enumerate())
创建线程本地数据
import threading if __name__ == "__main__": mydata = threading.local() mydata.x = 1 print("已创建本地线程{},其中本地存储的数据为{}".format(mydata, mydata.x))
-
-
锁
-
互斥锁
class threading.Lock
[2]原始锁处于 “锁定” 或者 “非锁定” 两种状态之一。它被创建时为非锁定状态。它有两个基本方法,
acquire()
和release()
。当状态为非锁定时,acquire()
将状态改为 锁定 并立即返回。当状态是锁定时,acquire()
将阻塞至其他线程调用release()
将其改为非锁定状态,然后acquire()
调用重置其为锁定状态并返回。release()
只在锁定状态下调用; 它将状态改为非锁定并立即返回。如果尝试释放一个非锁定的锁,则会引发RuntimeError
异常在开始系统的学习之前,我们先看下面一段代码
import time import threading def text1(num): global g_num for i in range(num): g_num += 1 def text2(num): global g_num for i in range(num): g_num += 1 def main(): t1 = threading.Thread(target=text1, args=(100, )) t2 = threading.Thread(target=text2, args=(100, )) print(g_num) if __name__ == "__main__": main()
可以发现返回了200,在小范围数字内多做了几次实验,发现都是如此,说明__线程之间的全局变量是共享的__
def text1(num): global g_num for i in range(num): g_num += 1 def text2(num): global g_num for i in range(num): g_num += 1 def main(): t1 = threading.Thread(target=text1, args=(100000, )) t2 = threading.Thread(target=text2, args=(100000, )) print(g_num) if __name__ == "__main__": main()
我们发现得出的结果和我们刚刚的结论有所不符,这位是为什么呢?
# 可以发现 当大数据运行时, 数值与预期不符合 # 这里就发生了资源竞争[1] """ [1]多线程任务进行时,全局变量是共享的 这就可能会导致在某一时刻多个线程同时调用同一全局变量的情况 (各个线程之间是相对独立的) """ # 这时候就需要一个新的概念————互斥锁了[2]
创建互斥锁对象
threading,Lock()
import threading import time lock = threading,Lock() def text1(num): global g_num try: num = int(num) for i in range(num): mes = "the state of block whther is locked or not is %s" % block.locked() print(mes) try: block.acquire(blocking=True, timeout=1) g_num += 1 block.release() except RuntimeError: print("没有上锁哦,不要随意解锁") except Exception as e: print("输入数据类型无法转化为整形对象") """ threading.Lock().aquire(blocking=True, timeout=1) 锁上互斥锁 :param timeout 互斥锁最大延迟时间 :param blocking 互斥锁的初始状态 threading.Lock().release() 打开互斥锁 threading.Lock().locked() 查看互斥锁的状态 """ def text2(num): global g_num try: num = int(num) for i in range(num): mes = "the state of block whther is locked or not is %s" % block.locked() print(mes) try: block.acquire(blocking=True, timeout=1) g_num += 1 block.release() except RuntimeError: print("没有上锁哦,不要随意解锁") except Exception as e: print("输入数据类型无法转化为整形对象") def main(): t1 = threading.Thread(target=text1, args=(100000, )) t2 = threading.Thread(target=text2, args=(100000, )) time.sleep(5) print(g_num) if __main__ == "__main__"": main()
- 阻塞或非阻塞的获取锁
threading.Lock().acquire(blocking=True/*初始化锁的是否处于堵塞状态*/,timeout=1/*超时回响*/)
- 释放一个锁:
threading.Lock().release()
- 获取锁的状态的布尔返回真值:
threading.Lock().locked()
互斥锁的出现,使得同一全局变量不能同时在多个线程被使用,保证了一定程度上变量的可靠,但也大大降低了代码的运行速度
### 特殊情况——死锁
import threading lock1 = threading.Lock() lock2 = threading.Lock() def test1(): lock1.acquire() for i in range(10): lock2.acquire() print("current value:{}".format(i)) lock2.release() lock1.release() def test2(): lock2.acquire() for i in range(10): lock1.acquire() print("current value:{}".format(i)) lock1.release() lock2.release() def main(): t1 = threading.Thread(target=test1) t2 = threading.Thread(target=test2) t1.start() t2.start() if __name__ == "__main__": main()
- 阻塞或非阻塞的获取锁
-
递归锁
class threading.RLock
import threading import time lock = threading.Lock() lock1 = threading.Lock() Rlock = threading.RLock() temp = 0 def index1(): global temp for i in range(10): lock.acquire() print(lock) temp += 1 lock.release() def index2(): global temp for i in range(10): Rlock.acquire() print(Rlock) temp += 1 Rlock.release() def main(): global temp t1 = threading.Thread(target=index1) t2 = threading.Thread(target=index2) t1.start() t2.start() time.sleep(2) print(temp) if __name__=="__main__": main()
threading.RLock()中的注意点
:threading.RLock().start()
当一个程已经拥有锁,递归级别增加一,并立即返回。否则,如果其他线程拥有该锁,则阻塞至该锁解锁。一旦锁被解锁(不属于任何线程),则抢夺所有权,设置递归等级为一,并返回。如果多个线程被阻塞,等待锁被解锁,一次只有一个线程能抢到锁的所有权。在这种情况下,没有返回值。
-
-
各种继承对象
-
定时器对象
class Threading.timer(interval, function, args=None, kwargs=None)
此类表示一个操作应该在等待一定的时间之后运行 — 相当于一个定时器。
Timer
类是Thread
类的子类,因此可以像一个自定义线程一样工作from threading import * def hello(): print("hello, world") def main() t = Timer(30.0, hello) t.start() if __name__ == "__main__": main()
其他方法:
cancel()
停止定时器并取消执行计时器将要执行的操作。仅当计时器仍处于等待状态时有效
-
事件对象
class threading.Event
通过网上的一个实例:
# ---- Event # ---- 捉迷藏的游戏 import threading, time class Hider(threading.Thread): def __init__(self, cond, name): super(Hider, self).__init__() self.cond = cond self.name = name def run(self): time.sleep(1) # 确保先运行Seeker中的方法 print(self.name + ': 我已经把眼睛蒙上了') self.cond.set() time.sleep(1) self.cond.wait() print(self.name + ': 我找到你了 ~_~') self.cond.set() print(self.name + ': 我赢了') class Seeker(threading.Thread): def __init__(self, cond, name): super(Seeker, self).__init__() self.cond = cond self.name = name def run(self): self.cond.wait() print(self.name + ': 我已经藏好了,你快来找我吧') self.cond.set() time.sleep(1) self.cond.wait() print(self.name + ': 被你找到了,哎~~~') def main(): cond = threading.Event() seeker = Seeker(cond, 'seeker') hider = Hider(cond, 'hider') seeker.start() hider.start() if __name__ == "__main__": main()
函数 说明 is_set() 当且仅当内部标识为 true 时返回 True
set() 将内部标识设置为 true 。所有正在等待这个事件的线程将被唤醒。当标识为 true 时,调用 wait()
方法的线程不会被被阻塞clear() 将内部标识设置为 false 。之后调用 wait()
方法的线程将会被阻塞wait(timeout=None) 阻塞线程直到内部变量为 true 。如果调用时内部标识为 true,将立即返回。否则将阻塞线程,直到调用 set()
方法将标识设置为 true 或者发生可选的超时-
class threading.Condition
def mes_obj(): t1 = threading.Thread(target=sum_all) t1.start() def sum_all(): condition.acquire() with condition: while not f(input("请输入你想输入的内容>>")): condition.wait(timeout=1.) """ wait_for(predicate, timeout=None) 等待,直到条件计算为真 predicate 应该是一个可调用对象而且它的返回值可被解释为一个布尔值 可以提供 timeout 参数给出最大等待时间 while not predicate(): cv.wait() notify(n=1) 默认唤醒一个等待这个条件的线程 如果调用线程在没有获得锁的情况下调用这个方法,会引发 RuntimeError 异常 notify_all() 唤醒所有正在等待这个条件的线程 """ print("能运行了") with condition: condition.notify() condition.release() def f(mes): if mes: return True else: return False
参数 说明 wait
(timeout=None)等待直到被通知或发生超时。如果线程在调用此方法时没有获得锁,将会引发 RuntimeError
异常wait_for
(predicate, timeout=None)等待,直到条件计算为真。 predicate 应该是一个可调用对象而且它的返回值可被解释为一个布尔值。可以提供 timeout 参数给出最大等待时间 notify
(n=1)默认唤醒一个等待这个条件的线程。如果调用线程在没有获得锁的情况下调用这个方法,会引发 RuntimeError
异常notify_all
()唤醒所有正在等待这个条件的线程 -
栅栏对象
class Barrier
(parties, action=None, timeout=None)-
class
threading.``Barrier
(parties, action=None, timeout=None)函数 wait
(timeout=None)可用于从所有线程中选择唯一的一个线程执行一些特别的工作 reset
()重置栅栏为默认的初始态。如果栅栏中仍有线程等待释放,这些线程将会收到 BrokenBarrierError
异常。abort() 使栅栏处于损坏状态。 这将导致任何现有和未来对 wait()
的调用失败并引发BrokenBarrierError
。parties 冲出栅栏所需要的线程数量 n_waiting 当前时刻正在栅栏中阻塞的线程数量 broken 一个布尔值,值为 True
表明栅栏为破损态创建一个需要 parties 个线程的栅栏对象。如果提供了可调用的 action 参数,它会在所有线
-
-
-
-
进程
-
先看个实例
import multiprocessing def f(x): return x*x def main(): with multiprocessing.Pool(5) as p: print(p.map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) if __name__ == "__main__": main()
上下文和启动方式
模式 说明 spawn 父进程启动一个新的Python解释器进程。子进程只会继承那些运行进程对象的 run()
方法所需的资源。特别是父进程中非必须的文件描述符和句柄不会被继承。相对于使用 fork 或者 forkserver,使用这个方法启动进程相当慢fork 父进程使用 os.fork()
来产生 Python 解释器分叉。子进程在开始时实际上与父进程相同。父进程的所有资源都由子进程继承。请注意,安全分叉多线程进程是棘手的forkserver 程序启动并选择* forkserver * 启动方法时,将启动服务器进程。从那时起,每当需要一个新进程时,父进程就会连接到服务器并请求它分叉一个新进程。分叉服务器进程是单线程的,因此使用 os.fork()
是安全的-
选择一个启动方式:在
if __name__ == '__main__'
中调用multiprocessing.set_start_method(mode)
来获取上下文请尽量避免
multiprocessing.set_start_method()
的使用
import multiprocessing import os def info(title): print(title) print("module name", __name__) print("parent process", os.getppid()) print("process id", os.getpid()) def f(name): info("function f") print("hello", name) def foo(q): q.put("hello") if __name__ == '__main__': multiprocessing.set_start_method('spawn') q = multiprocessing.Queue() p = multiprocessing.Process(target=foo, args=(q, )) p.start() print(q.get()) p.join()
也可以使用
multiprocessing.get_context()
来获取上下文在进程之间交换对象
-
队列
Queue类是一个近似queue.Queue的克隆
import multiprocessing def f(q): q.put([42, None, 'hello']) if __name__ == "__main__": q = multiprocessing.Queue() p = multiprocessing.Process(target=f, args=(q, )) p.start() print(q.get()) p.join()
队列是线程和进程安全
-
multiprocessing.SimpleQueue
是一个简化的Queue类方法:
-
multiprocessing.SimpleQueue().close()
关闭队列:释放内存
-
multiprocessing.SimpleQueue().empty()
判断队列是否为空
-
multiprocessing.SimpleQueue().get()
从队列中移除并返回一个对象
-
multiprocessing.SimpleQueue().put()
将item放入队列
import multiprocessing def make_simple_queue(): q = multiprocessing.SimpleQueue() print(q.empty()) q.put({ 'name': 'Mike'}) print(q.empty()) q.get() print(q.empty()) if __name__ == "__main__": make_simple_queue()
-
multiprocessing.Queue([maxsize])
返回一个使用一个管道和少量锁和信号量实现的共享队列实例方法:
-
multiprocessing.Queue().qsize()
返回队列的大致长度(由于多线程或多进程的上下文,所以不可靠)
-
`multiprocessing.Queue().empty()
返回队列是否为空
-
multiprocessing.Queue().full()
返回队列是否为满
-
multiprocessing.Queue().get([block[,timeout]])
从队列中取出并返回对象,
-
multiprocessing.Queue().get_nowait()
相当于get(False)
-
multiprocessing.Queue().put(obj[, block[, timeout]])
将obj放入队列
-
multiprocessing.Queue().put_nowait(obj)
相当于put(obj, False)
-
multiprocessing.Queue().close()
指示当前进度将不再往队列中放入对象
-
multiprocessing.Queue().join_thread()
等待后台线程,这个方法仅在调用了close()方法之后可用,这会阻塞当前进程,直到后台线程退出
-
multiprocessing.Queue().cancel_join_thread()
防止join_thread()方法阻塞当前进程
import multiprocessing def make_queue(): q = multiprocessing.Queue(5) print("当前队列状态是否为空{},队列中数据元素的个数为{}".format(q.empty(), q.qsize())) q.put({ 'name': 'Mike', 'age': 10}) print("当前队列状态是否为空{},队列中数据元素的个数为{}".format(q.empty(), q.qsize())) print("当前获获取数据的为{}".format(q.get())) print("当前队列状态是否为空{},队列中数据元素的个数为{}".format(q.empty(), q.qsize())) q.put({ 'name': 'Mike', 'age': 10}) q.put({ 'name': 'John', 'age': 10}) q.put({ 'name': 'Alice', 'age': 10}) q.put({ 'name': 'Google', 'age': 10}) q.put({ 'name': 'FireFox', 'age': 10}) print("当前队列是否已满{}".format(q.full())) print(q.get()) print("当前队列是否已满{}".format(q.full())) q.close() q.join_thread() q.cancel_join_thread() if __name__ == "__main__": make_queue()
-
-
-
-
管道
multiprocessing.Pipe([duplex])
返回一对Connection对象(conn1, conn2),分别表示管道两端
如果duplex被设置为True(默认值),那么该管道是双向。如果duplex被设置为False,那么该管道是单向的
-
进程
multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
进程对象表示在单独进程中运行的活动
方法:
-
multiprocessing.Process().run()
表示进程活动的方法
-
multiprocessing.Process().start()
启动进程活动,会将对象的run()方向安排在一个单独的进程中调用
-
multiprocessing.Process().name
获取进程的名称,该名称是一个字符串,及用于识别目的
-
multiprocessing.Process().is_alive()
返回进程是否还活着
-
multiprocessing.Process().daemon
判断进程是否是守护进程
-
multiprocessing.Process().join([timeout])
是当前进程进入堵塞状态,进程无法join自身,这会导致死锁
-
multiprocessing.Process().pid
返回进程ID
-
multiprocessing.Process().authkey
进程的身份验证密钥(字节字符串)
-
multiprocessing.Process().sentinel
系统对象的数字句柄,当前进程结束时将变为"ready"
-
multiprocessing.Process().kill()
终止进程
-
multiprocessing.Process().terminate()
终止进程
-
multiprocessing.Process().close()
关闭Process对象,释放与之关联的所有资源
-
multiprocessing.Process().exitcode
子进程退出的代码,如果进程尚未中止,这将是None。负值-N表示子进程被信号N中止
-
其他类属性:
-
exception
multiprocessing.``ProcessError
所有
multiprocessing
异常的基类。 -
exception
multiprocessing.``BufferTooShort
当提供的缓冲区对象太小而无法读取消息时,
Connection.recv_bytes_into()
引发的异常。如果e
是一个BufferTooShort
实例,那么e.args[0]
将把消息作为字节字符串给出。 -
exception
multiprocessing.``AuthenticationError
出现身份验证错误时引发。
-
exception
multiprocessing.``TimeoutError
有超时的方法超时时引发。
其它类方法:
-
multiprocessing.active_children()
返回当前进程存活的子进程的列表
-
multiprocessing.cpu_count()
返回当前系统的CPU数量
-
multiprocessing.current_process()
返回当前进程相对应的Process对象
-
multiprocessing.parent_process()
返回父进程Prograss对象
-
multiprocessing.get_all_start_method()
返回支持的启动上下文的方法列表
-
multipricessing.freeze_support()
提供冻结以产生Windows可执行文件的支持
-
multipricessing.get_all_start_methods()
返回支持的启动方法的列表
-
multipricessing.get_start_method()
返回进程时使用的启动方法名
-
multipricessing.get_context(method=None)
返回一个Context对象,进程支持的上下文启动方法
-
multipricessing.set_start_method(method)
设置启动子进程的方法(最多只能调用1次)
-