什么是线程
在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程
线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程
车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线
流水线的工作需要电源,电源就相当于cpu
所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。
多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。
线程的创建开销小
创建进程的开销要远大于线程?
如果我们的软件是一个工厂,该工厂有多条流水线,流水线工作需要电源,电源只有一个即cpu(单核cpu)
一个车间就是一个进程,一个车间至少一条流水线(一个进程至少一个线程)
创建一个进程,就是创建一个车间(申请空间,在该空间内建至少一条流水线)
而建线程,就只是在一个车间内造一条流水线,无需申请空间,所以创建开销小
进程之间是竞争关系,线程之间是协作关系?
车间直接是竞争/抢电源的关系,竞争(不同的进程直接是竞争关系,是不同的程序员写的程序运行的,迅雷抢占其他进程的网速,360把其他进程当做病毒干死)
一个车间的不同流水线式协同工作的关系(同一个进程的线程之间是合作关系,是同一个程序写的程序内开启动,迅雷内的线程是合作关系,不会自己干自己)
为何要用多线程
多线程指的是,在一个进程中开启多个线程,简单的讲:如果多个任务共用一块地址空间,那么必须在一个进程内开启多个线程。详细的讲分为4点:
- 多线程共享一个进程的地址空间
- 线程比进程更轻量级,线程比进程更容易创建可撤销,在许多操作系统中,创建一个线程比创建一个进程要快10-100倍,在有大量线程需要动态和快速修改时,这一特性很有用
- 若多个线程都是cpu密集型的,那么并不能获得性能上的增强,但是如果存在大量的计算和大量的I/O处理,拥有多个线程允许这些活动彼此重叠运行,从而会加快程序执行的速度。
- 在多cpu系统中,为了最大限度的利用多核,可以开启多个线程,比开进程开销要小的多。(这一条并不适用于python)
Python 实现多线程编程需要借助于 threading 模块。
threading 模块中最核心的内容是 Thread 这个类。
import threading
我们要创建 Thread 对象,然后让它们运行,每个 Thread 对象代表一个线程,在每个线程中我们可以让程序处理不同的任务,这就是多线程编程。
值得注意的是,程序运行时默认就是在主线程上
创建 Thread 对象有 2 种手段。
- 直接创建 Thread ,将一个 callable 对象从类的构造器传递进去,这个 callable 就是回调函数,用来处理任务。
- 编写一个自定义类继承 Thread,然后复写 run() 方法,在 run() 方法中编写任务处理代码,然后创建这个 Thread 的子类。
1. 直接创建 Thread 对象。
class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
#args 是固定参数,为元组类型,kwargs 是可变参数。
Thread 的构造方法中,最重要的参数是 target
,所以我们需要将一个 callable 对象赋值给它,线程才能正常运行。
如果要让一个 Thread 对象启动,调用它的 start() 方法
就好了。
import threading
import time
def test():
for i in range(5):
print('test',i)
time.sleep(1)
thread=threading.Thread(target=test)
thread.start()
for i in range(5):
print('main',i)
time.sleep(1)
#打印
test 0
main 0
main 1
test 1
main 2
test 2
main 3
test 3
main 4
test 4
Thread 的名字
每一个 Thread 都有一个 name 的属性,代表的就是线程的名字,这个可以在构造方法中赋值。
如果在构造方法中没有个 name 赋值的话,默认就是 “Thread-N” 的形式,N 是数字。
import threading
import time
def test():
for i in range(5):
print(threading.current_thread().name+'test',i)
time.sleep(1)
thread=threading.Thread(target=test)
thread.start()
for i in range(5):
print(threading.current_thread().name + 'main', i)
time.sleep(1)
##打印
Thread-1 test 0
MainThread main 0
Thread-1 test 1
MainThread main 1
Thread-1 test 2
MainThread main 2
Thread-1 test 3
MainThread main 3
Thread-1 test 4
MainThread main 4
如果我们在 Thread 对象创建时,构造方法里面赋值。
thread = threading.Thread(target=test,name='TestThread')
打印
TestThread test 0
MainThread main 0
MainThread main 1
TestThread test 1
MainThread main 2
TestThread test 2
MainThread main 3
TestThread test 3
MainThread main 4
TestThread test 4
Thread 的生命周期
Thread 的生命周期
- 创建对象时,代表 Thread 内部被初始化。
- 调用 start() 方法后,thread 会开始运行。
- thread 代码正常运行结束或者是遇到异常,线程会终止。
可以通过 Thread 的 is_alive() 方法
查询线程是否还在运行。
join() 提供线程阻塞手段
上面代码两个线程是同时运行的,但如果让一个先运行,一个后运行,怎么做呢?
调用一个 Thread 的 join() 方法
,可以阻塞自身所在的线程。
import threading
import time
def test():
for i in range(5):
print(threading.current_thread().name+' test ',i)
time.sleep(0.5)
thread = threading.Thread(target=test,name='TestThread')
thread.start()
thread.join()
for i in range(5):
print(threading.current_thread().name+' main ', i)
print(thread.name+' is alive ', thread.isAlive())
time.sleep(1)
##打印
# TestThread test 0
# TestThread test 1
# TestThread test 2
# TestThread test 3
# TestThread test 4
# MainThread main 0
# TestThread is alive False
# MainThread main 1
# TestThread is alive False
# MainThread main 2
# TestThread is alive False
# MainThread main 3
# TestThread is alive False
# MainThread main 4
# TestThread is alive False
默认的情况是,join() 会一直等待对应线程的结束,但可以通过参数赋值,等待规定的时间就好了。
def join(self, timeout=None):
timeout 是一个浮点参数,单位是秒。
如果我们更改上面的代码。
thread.join(1.0)
TestThread test 0
TestThread test 1
MainThread main 0
TestThread is alive True
TestThread test 2
TestThread test 3
MainThread main 1
TestThread is alive True
TestThread test 4
MainThread main 2
TestThread is alive False
MainThread main 3
TestThread is alive False
MainThread main 4
TestThread is alive False
Thread 中的 daemon 属性
Thread 的构造方法中有一个 daemon 参数。默认是 None。
daemon 起什么作用呢?
# 主线程执行代码的时长比 TestThread 要短。
import threading
import time
def test():
for i in range(5):
print(threading.current_thread().name+' test ',i)
time.sleep(2)
thread = threading.Thread(target=test,name='TestThread')
# thread = threading.Thread(target=test,name='TestThread',daemon=True)
thread.start()
for i in range(5):
print(threading.current_thread().name+' main ', i)
print(thread.name+' is alive ', thread.isAlive())
time.sleep(1)
# 打印
TestThread test 0
# MainThread main 0
# TestThread is alive True
# MainThread main 1
# TestThread is alive True
# TestThread test 1
# MainThread main 2
# TestThread is alive True
# MainThread main 3
# TestThread is alive True
# TestThread test 2
# MainThread main 4
# TestThread is alive True
# TestThread test 3
# TestThread test 4
MainThread 没有代码运行的时候,TestThread 还在运行。
这是因为 MainThread 在等待其他线程的结束。
TestThread 中 daemon 属性默认是 False,这使得 MainThread 需要等待它的结束,自身才结束。
如果要达到,MainThread 结束,子线程也立马结束,怎么做呢?
其实很简单,只需要在子线程调用 start() 方法之前设置 daemon 就好了。
当然也可以在子线程的构造器中传递 daemon 的值为 True。
thread = threading.Thread(target=test,name='TestThread',daemon=True)
# thread.setDaemon(True)
#打印
# TestThread test 0
# MainThread main 0
# TestThread is alive True
# MainThread main 1
# TestThread is alive True
# TestThread test 1
# MainThread main 2
# TestThread is alive True
# MainThread main 3
# TestThread is alive True
# TestThread test 2
# MainThread main 4
# TestThread is alive True
2.自定义类继承 Thread
自定义一个 Thread 的子类,然后复写它的 run() 方法。
import threading
import time
# 自定义线程类,继承自Thread类并重写其run方法
class TestThread(threading.Thread):
def __init__(self,name=None):
# 继承父类__init__方法
threading.Thread.__init__(self,name=name)
# 重写run方法 定义每个线程都要运行的函数
def run(self):
for i in range(5):
print(threading.current_thread().name+'test',i)
time.sleep(1)
thread=TestThread(name='TestThread')
thread.start()
for i in range(5):
print(threading.current_thread().name+'mian',i)
print(thread.name+'is alive',thread.isAlive())
time.sleep(1)
# 打印
# TestThread test 0
# MainThread main 0
# TestThread is alive True
# TestThread test 1
# MainThread main 1
# TestThread is alive True
# TestThread test 2
# MainThread main 2
# TestThread is alive True
# MainThread main 3
# TestThread is alive True
# TestThread test 3
# MainThread main 4
# TestThread test 4
# TestThread is alive True
为了让线程代码更好的封装。可以使用threading模块下的Thread类,继承自这个类,然后实现run方法,线程就会自动运行run方法中的代码。
import threading
import time
class CodingThread(threading.Thread):
def run(self):
for x in range(3):
print('%s正在写代码' % threading.current_thread())
time.sleep(1)
class DrawingThread(threading.Thread):
def run(self):
for x in range(3):
print('%s正在画图' % threading.current_thread())
time.sleep(1)
def multi_thread():
t1 = CodingThread()
t2 = DrawingThread()
t1.start()
t2.start()
if __name__ == '__main__':
multi_thread()
线程相关的其他方法
Thread实例对象的方法
- isAlive(): 返回线程是否活动的。
- getName(): 返回线程名。
- setName(): 设置线程名。
threading模块提供的一些方法:
- threading.currentThread(): 返回当前的线程变量。
- threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
- threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
import threading #线程
import time
def Hi(num):
print("hello %d"%num)
time.sleep(3)
if __name__ == '__main__':
t1=threading.Thread(target=Hi,args=(10,))#创建了一个线程对象t1
t1.start()
t2 = threading.Thread(target=Hi, args=(9,)) # 创建了一个线程对象t2
t2.start()
print("ending..........")
#打印
hello 10
hello 9
ending..........