文章目录
一、what is 线程 ?
线程是操作系统能够进行运算调度的最小单位。线程被包含在进程中,是进程中实际处理单位。一条线程就是一堆指令集合。一条线程是指进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
二、what is 进程 ?
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
三、线程 VS 进程
(1)进程(process)是一块包含了某些资源的内存区域。操作系统利用进程把它的工作划分为一些功能单元。
(2)进程中所包含的一个或多个执行单元称为线程(thread)。进程还拥有一个私有的虚拟地址空间,该空间仅能被它所包含的线程访问。
(3)线程只能归属于一个进程并且它只能访问该进程所拥有的资源。当操作系统创建一个进程后,该进程会自动申请一个名为主线程或首要线程的线程。
(4)线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
(5)处理IO密集型任务或函数用线程;多线程采用的是分时复用技术,即不存在真正的多线程,cpu做的事是快速地切换线程,以达到类似同步运行的目的,因为高密集运算方面多线程是没有用的,但是对于存在延迟的情况(延迟IO,网络等)多线程可以大大减少等待时间,避免不必要的浪费。
(6)处理计算密集型任务或函数用进程,每个cpu可以执行一个进程,不需要进程间切换。
(7)详见第七条/八条分析…
四、创建线程
Thread 是threading模块中最重要的类之一,可以使用它来创建线程。有两种方式来创建线程:
sleep的时候是不会占用cpu的,在sleep的时候操作系统会把线程暂时挂起。
1.普通创建
通过threading.Thread()创建线程,其中target接收的是要执行的函数名字,args接收传入函数的参数,以元组的形式表示。
import threading
import time
def run(n):
print("task", n)
time.sleep(1)
print('2s')
time.sleep(1)
print('1s')
time.sleep(1)
print('0s')
time.sleep(1)
t1 = threading.Thread(target=run, args=("t1",))
t2 = threading.Thread(target=run, args=("t2",))
t1.start()
t2.start()
结果:
task t1
task t2
2s
2s
1s
1s
0s
0s
- 以上结果每2行先打印,说明2个线程都同时执行了,使用不同的CPU
2.继承Thread,重构run方法
- python的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。
- 而创建自己的线程实例后,通过Thread类的start方法,可以启动该线程,交给python虚拟机进行调度,当该线程获得执行的机会时,就会调用run方法执行线程。
import threading
import time
class MyThread(threading.Thread):
def __init__(self, n):
super(MyThread, self).__init__() # 重构run函数必须要写
self.n = n
def run(self):
print("task", self.n)
time.sleep(1)
print('2s')
time.sleep(1)
print('1s')
time.sleep(1)
print('0s')
time.sleep(1)
if __name__ == "__main__":
t1 = MyThread("t1")
t2 = MyThread("t2")
t1.start()
t2.start()
结果:
task t1
task t2
2s
2s
1s
1s
0s
0s
- 以上结果每2行先打印,说明2个线程都同时执行了,使用不同的CPU
- 运行的结果和打印效果与普通创建一样
五、主线程&子线程
- 当一个进程启动之后,会默认产生一个主线程,因为线程是程序执行流的最小单元.
- 当设置多线程时,主线程会创建多个子线程。
- 在python中,默认情况下(其实就是setDaemon(False) ),主线程执行完自己的任务以后,就退出了。但此时子线程会继续执行自己的任务,直到自己的任务结束。见案例一
- 如果设置子线程为守护线程(setDaemon(True) ),则主线程一旦执行结束,则全部线程全部被终止执行,可能出现的情况就是,子线程的任务还没有完全执行结束,就被迫停止。见案例二
1.默认情况下的主线程&子线程----主程序需要等待子线程
import threading
import time
def run():
time.sleep(2)
print('当前线程的名字是: ', threading.current_thread().name)
time.sleep(2)
if __name__ == '__main__':
start_time = time.time()
print('这是主线程:', threading.current_thread().name)
thread_list = []
for i in range(5):
t = threading.Thread(target=run)
thread_list.append(t)
for t in thread_list:
t.start()
print('主线程结束!' , threading.current_thread().name)
print('一共用时:', time.time()-start_time)
这是主线程: MainThread # 首先打印前面三行
主线程结束! MainThread
一共用时: 0.001096963882446289
当前线程的名字是: Thread-1 # 等待2s左右,再执行后面5行
当前线程的名字是: Thread-2
当前线程的名字是: Thread-4
当前线程的名字是: Thread-3
当前线程的名字是: Thread-5
Process finished with exit code 0 # 等待大约2s,程序执行完毕
- 如上案例的分析,说明只要运行程序,都会有个主线程
- 创建的子线程运行与主线程,默认情况下互不影响,主线程执行完了,子线程还可以继续执行。
2.设置子线程为守护线程setDaemon(True)—主程序不需要等待子线程
import threading
import time
def run():
time.sleep(2)
print('当前线程的名字是: ', threading.current_thread().name)
time.sleep(2)
if __name__ == '__main__':
start_time = time.time()
print('这是主线程:', threading.current_thread().name)
thread_list = []
for i in range(5):
t = threading.Thread(target=run)
thread_list.append(t)
for t in thread_list:
t.setDaemon(True) # 设置守护线程,未启动的线程才能设置守护线程
t.start()
print('主线程结束了!', threading.current_thread().name)
print('一共用时:', time.time() - start_time)
结果:
这是主线程: MainThread # 运行程序,直接执行了前三行(主线程)
主线程结束了! MainThread
一共用时: 0.0007190704345703125
Process finished with exit code 0 # 执行完,立即结束程序
- 通过以上案例,发现子线程设置为守护线程之后,主线程执行完,程序就结束了。
子线程无法完整得到执行。
3.设置阻塞状态join—主线程等待子线程一定时间再执行
-
阻塞主进程和主线程,只要子线程设置了join方法,主线程就需要等待此子线程,适合依次执行多子线程
-
多个子线程join的情况下,依次执行各子线程的join方法。前头一个结束了才能执行后面一个。这里的依次执行,是有计算内容的时候,如果是sleep状态,则相当于程序挂起,空置状态,需要区别对待
-
json里面timeout无参数,为无限等待,知道该线程结束,才开始执行下一个线程的join。
-
参数timeout为线程的阻塞时间,如 timeout=2 ,就是主线程会等待这个线程2s ,2s之后就不管他了,线程被杀死,继续执行其他子线程或主线程
import threading
import time
def run():
time.sleep(2)
print('当前线程的名字是: ', threading.current_thread().name)
time.sleep(2)
if __name__ == '__main__':
start_time = time.time()
print('这是主线程:', threading.current_thread().name)
thread_list = []
for i in range(5):
t = threading.Thread(target=run)
thread_list.append(t)
for t in thread_list:
t.start()
t.join()
print('主线程结束了!', threading.current_thread().name)
print('一共用时:', time.time() - start_time)
结果:
这是主线程: MainThread # 先执行这一行,主线程
当前线程的名字是: Thread-1 # 每个线程依次执行,间隔2s
当前线程的名字是: Thread-2
当前线程的名字是: Thread-3
当前线程的名字是: Thread-4
当前线程的名字是: Thread-5
主线程结束了! MainThread # 最后执行主线程的打印
一共用时: 20.027244091033936
- 通过如上分析, 主程序、主线程都等待了被设置为join的子线程,等待子线程执行完之后,主程序、主线程才去执行。
- 如果子线程不设置守护线程,只要设置join状态,最终主程序、主线程还是会等待子线程。
六、子线程里setDaemon(True) && join(timeout)同时使用
- 当设置守护线程时setDaemon(True),意思是子线程是否完整运行对主线程不重要了,主线程结束,子线程也就不执行了。
- 当设置子线程阻塞join(timeout)状态时,意思是主线程必须等待子线程,子线程运行时,主线程处于阻塞状态。
setDaemon() | join() | 结果 |
---|---|---|
False | None | 主线程与子线程执行互不影响,且都完整执行,直到所有线程执行完,程序结束 |
True | None | 主线程执行完,程序结束,子线程是否能完整执行不确定 |
True | join() | 1.先设置setDaemon和start()线程,其次join线程,则会依次执行线程,但是最终哪个线程先执行不确定,因为线程会强占资源;见如下案例一 2.先设置setDaemon,然后再start()线程和join每个子线程,则会依次执行每个字线程;见如下案例二 3.先start()线程并设置join,然后setDaemon线程会报错,因为join执行完毕了,线程处于活跃状态,不能setDaemon了;见如下案例三 |
True | join(n) | 1.先设置setDaemon和start()线程,其次join(n)线程,则会依次执行线程,但是最终哪个线程先执行不确定,因为线程会强占资源,但每个子线程可执行的时间由n确定; 2.先设置setDaemon,然后start()线程和join(n)每个子线程,则会依次执行每个字线程,每个子线程执行n秒 3.先start()线程并设置join(n),然后setDaemon线程会报错,因为join(n)执行完毕了,线程处于活跃状态,不能setDaemon了。 |
False | join() | 子线程依次执行,然后主线程才会执行完毕,主进程才结束 |
False | join(n) | 子线程依次执行,且子线程只执行N秒,然后主线程才会执行完毕,主进程才结束 |
1.先设置setDaemon和start()线程,其次join线程
import threading
import time
def run():
time.sleep(2)
print('当前线程的名字是: ', threading.current_thread().name)
time.sleep(2)
if __name__ == '__main__':
start_time = time.time()
print('这是主线程:', threading.current_thread().name)
thread_list = []
for i in range(5):
t = threading.Thread(target=run)
thread_list.append(t)
for t in thread_list:
t.setDaemon(True)
t.start()
for t in thread_list:
t.join()
print('主线程结束了!', threading.current_thread().name)
print('一共用时:', time.time() - start_time)
这是主线程: MainThread
当前线程的名字是: Thread-1 # 5个线程基本同时打印的,先执行哪个不确定
当前线程的名字是: Thread-3 # 线程之间会强占资源,谁先抢到谁先执行
当前线程的名字是: Thread-5
当前线程的名字是: Thread-2
当前线程的名字是: Thread-4
主线程结束了! MainThread
一共用时: 4.005864858627319
Process finished with exit code 0
2.先设置setDaemon,然后再start()线程和join每个子线程
import threading
import time
def run():
time.sleep(2)
print('当前线程的名字是: ', threading.current_thread().name)
time.sleep(2)
if __name__ == '__main__':
start_time = time.time()
print('这是主线程:', threading.current_thread().name)
thread_list = []
for i in range(5):
t = threading.Thread(target=run)
thread_list.append(t)
for t in thread_list:
t.setDaemon(True)
for t in thread_list:
t.start()
t.join()
print('主线程结束了!', threading.current_thread().name)
print('一共用时:', time.time() - start_time)
这是主线程: MainThread
当前线程的名字是: Thread-1 # 各个子线程依次执行
当前线程的名字是: Thread-2
当前线程的名字是: Thread-3
当前线程的名字是: Thread-4
当前线程的名字是: Thread-5
主线程结束了! MainThread # 最后执行主程序
一共用时: 10.025158882141113
Process finished with exit code 0
3.先start()线程并设置join,然后setDaemon线程会报错
import threading
import time
def run():
time.sleep(1)
print('当前线程的名字是: ', threading.current_thread().name)
time.sleep(1)
if __name__ == '__main__':
start_time = time.time()
print('这是主线程:', threading.current_thread().name)
thread_list = []
for i in range(5):
t = threading.Thread(target=run)
thread_list.append(t)
for t in thread_list:
t.start()
t.join()
for t in thread_list:
t.setDaemon(True)
print('主线程结束了!', threading.current_thread().name)
print('一共用时:', time.time() - start_time)
这是主线程: MainThread
当前线程的名字是: Thread-1 # 各个子线程依次执行
当前线程的名字是: Thread-2
当前线程的名字是: Thread-3
当前线程的名字是: Thread-4
当前线程的名字是: Thread-5
# 执行到setDaemon时候,报错,线程活跃状态不能设置守护线程
Traceback (most recent call last):
File "/Users/test/threadStudy/threadJoin.py", line 26, in <module>
t.setDaemon(True)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 1148, in setDaemon
self.daemon = daemonic
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 1141, in daemon
raise RuntimeError("cannot set daemon status of active thread")
RuntimeError: cannot set daemon status of active thread
参考文章:
https://www.cnblogs.com/whatisfantasy/p/6440585.html
https://www.cnblogs.com/cnkai/p/7504980.html#commentform