这篇文章主要介绍了python线程与线程池,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
线程
进程和线程
什么是进程?
进程就是正在运行的程序, 一个任务就是一个进程, 进程的主要工作是管理资源, 而不是实现功能
什么是线程?
线程的主要工作是去实现功能, 比如执行计算.
线程和进程的关系就像员工与老板的关系,
老板(进程) 提供资源 和 工作空间,
员工(线程) 负责去完成相应的任务
特点
一个进程至少由一个线程, 这一个必须存在的线程被称为主线程, 同时一个进程也可以有多个线程, 即多线程
当我们我们遇到一些需要重复执行的代码时, 就可以使用多线程分担一些任务, 进而加快运行速度
线程的实现
线程模块
Python通过两个标准库_thread和threading, 提供对线程的支持 , threading对_thread进行了封装。
threading模块中提供了Thread , Lock , RLock , Condition等组件。
因此在实际的使用中我们一般都是使用threading来实现多线程
线程包括子线程和主线程:
主线程 : 当一个程序启动时 , 就有一个线程开始运行 , 该线程通常叫做程序的主线程
子线程 : 因为程序是开始时就执行的 , 如果你需要再创建线程 , 那么创建的线程就是这个主线程的子线程
主线程的重要性体现在两方面 :
- 是产生其他子线程的线程
- 通常它必须最后完成执行, 比如执行各种关闭操作
Thread类
常用参数说明
参数 | 说明 |
---|---|
target | 表示调用的对象, 即子线程要执行的任务, 可以是某个内置方法, 或是你自己写的函数 |
name | 子线程的名称 |
args | 传入target函数中的位置参数, 是一个元组, 参数后必须加逗号 |
常用实例方法
方法 | 作用 |
---|---|
Thread.run(self) | 线程启动时运行的方法, 由该方法调用 target参数所指定的函数 |
Thread.start(self) | 启动进程, start方法就是区帮你调用run方法 |
Thread.terminate(self) | 强制终止线程 |
Thread.join(self, timeout=None) | 阻塞调用, 主线程进行等待 |
Thread.setDaemon(self, daemonic) | 将子线程设置为守护线程, 随主线程结束而结束 |
Thread.getName(self, name) | 获取线程名 |
Thread.setName(self, name) | 设置线程名 |
创建线程
在python中创建线程有两种方式, 实例Thread类和继承重写Thread类
实例Thread类
import threading import time def run(name, s): # 线程要执行的任务 time.sleep(s) # 停两秒 print('I am %s' % name) # 实例化线程类, 并传入函数及其参数, t1 = threading.Thread(target=run, name='one', args=('One', 5)) t2 = threading.Thread(target=run, name='two', args=('Two', 2)) # 开始执行, 这两个线程会同步执行 t1.start() t2.start() print(t1.getName()) # 获取线程名 print(t2.getName()) # Result: one two I am Two # 运行2s后 I am One # 运行5s后
继承Thread类
class MyThread(threading.Thread): # 继承threading中的Thread类 # 线程所需的参数 def __init__(self, name, second): super().__init__() self.name = name self.second = second # 重写run方法,表示线程所执行的任务,必须有 def run(self): time.sleep(self.second) print('I am %s' % self.name) # 创建线程实例 t1 = MyThread('One', 5) t2 = MyThread('Two', 2) # 启动线程,实际上是调用了类中的run方法 t1.start() t2.start() t1.join() print(t1.getName()) print(t2.getName()) # Result: I am Two # 运行后2s I am One # 运行后5s One Two
常用方法
join()
阻塞调用程序 , 直到调用join () 方法的线程执行结束, 才会继续往下执行
<a style="color:transparent">来源gao($daima.com搞@代@#码(网</a> # 开始执行, 这两个线程会同步执行 t1.start() t2.start() t1.join() # 等待t1线程执行完毕,再继续执行剩余的代码 print(t1.getName()) print(t2.getName()) # Result: I am Two I am One one two
setDemon()
使用给线程设置守护模式: 子线程跟随主线程的结束而结束, 不管这个子线程任务是否完成. 而非守护模式的子线程只有在执行完成后, 主线程才会执行完成
setDaemon() 与 join() 基本上是相对的 , join会等子线程执行完毕 ; 而setDaemon则不会等
def run(name, s): # 线程要执行的函数 time.sleep(s) # 停两秒 print('I am %s' % name) # 实例化线程类, 并传入函数及其参数 t1 = threading.Thread(target=run, name='one', args=('One', 5)) t2 = threading.Thread(target=run, name='two', args=('Two', 2)) # 给t1设置守护模式, 使其随着主线程的结束而结束 t1.setDaemon(True) # 开始执行, 这两个线程会同步执行 t1.start() t2.start() # 主线程会等待未设置守护模式的线程t2执行完成 # Result: I am Two # 运行后2s
线程间的通信
互斥锁
在同一个进程的多线程中 , 其中的变量对于所有线程来说都是共享的 , 因此 , 如果多个线程之间同时修改一个变量 , 那就乱套了 , 共享的数据就会有很大的风险 , 所以我们需要互斥锁 , 来锁住数据 , 防止篡改。
来看一个错误的示范:
a = 0 def incr(n): global a for i in range(n): a += 1 # 这两个方法同时声明了变量a,并对其进行修改 def decr(n): global a for i in range(n): a -= 1 t_incr = threading.Thread(target=incr, args=(1000000,)) t_decr = threading.Thread(target=decr, args=(1000000,)) t_incr.start() t_decr.start() t_incr.join() t_decr.join() print(a) # 期望结果应该是0, 但是因为这里没有设置互斥锁, 所以两个方法是同时对同一个变量进行修改, 得到的的结果值是随机的
下面我们改一下上面的代码 , 两个方法加上互斥锁:
a = 0 lock = threading.Lock() # 实例化互斥锁对象, 方便之后的调用 def incr(n): global a for i in range(n): lock.acquire() # 上锁的方法 a += 1 lock.release() # 解锁的方法 # 要注意的是上锁的位置是, 出现修改操作的代码 def decr(n): global a for i in range(n): with lock: # 也可以直接使用with, 自动解锁 a -= 1 t_incr = threading.Thread(target=incr, args=(1000000,))以上就是详解python中的线程与线程池的详细内容,更多请关注gaodaima搞代码网其它相关文章!