初识线程
在传统操作系统中,每一个进程就有一个地址空间,而且默认就有一个控制线程,cpu真正的执行单位是线程。
就好比
在工厂中,每个车间都有房子,这个房子就是内存空间,每个车间默认有一条流水线,就是线程。
操作系统==》工厂
进程==》车间
线程==》流水线
cpu==》电源
线程:cpu最小的执行单位
进程:资源集合/资源单位
线程运行=运行代码
进程运行=各种资源 +线程
运行:申请内存空间,先把解释器丢进去并把代码丢进去,运行代码。
进程和线程的区别:
线程==》单指代码的执行过程
进程==》资源的申请与销毁的过程
进程内存空间彼此隔离,同一个进程下的线程共享资源。
进程和线程的创建速度比较:
进程创建速度远小于线程创建速度
因为进程需要申请资源开辟空间,很慢,线程只是告诉操作系统一个执行方案,很快,不用开辟新空间。
开启子线程的两种方式
一、
from threading import Thread
import time
def task():
print('线程 start')
time.sleep(2)
print('线程 end')
if __name__ == '__main__':
t = Thread(target=task)
t.start() # 告诉操作系统开一个线程 .
print('主')
二、
from threading import Thread
import time
# 进程等待所有线程结束才会结束
class Myt(Thread):
def run(self):
print('子线程 start')
time.sleep(5)
print('子线程 end')
t = Myt()
t.start()
print('主线程')
会发现和进程很相似,几乎一样了。
那么也会有join方法了
线程的join方法
from threading import Thread
import time
def task():
print('子线程 start')
time.sleep(2)
print('子线程 end')
t = Thread(target=task)
t.start()
t.join() # 等待子线程运行结束
print('主线程')
和进程的是一样的,等待某个子线程结束后,在join的阻塞才会结束。
空间共享
from threading import Thread
import time,os
x = 100
def task():
global x
x = 50
print(os.getpid()) # 5204
if __name__ == '__main__':
t = Thread(target=task)
t.start()
time.sleep(2)
print(x) # 50
print(os.getpid()) # 5204
多个子线程共享同一块内存空间,也就是同一块进程的空间。一个子线程对空间中的对象进行了改变操作,别的子线程访问到的也是已经被改变过的东西了。
当进程join遇见线程join
from multiprocessing import Process
from threading import Thread
import time
def task():
print('进程 开启')
time.sleep(10)
print('进程 结束')
def task2():
print('子线程 开启')
# time.sleep(2)
print('子线程 结束')
if __name__ == '__main__':
p = Process(target=task)
t = Thread(target=task2)
t.start() # 开线程
p.start() # 开进程
print('子进程join开始')
p.join() # 主进程的主线程等待子进程运行结束
print('主')
主进程里的p.join()只会阻塞住主线程,而不会阻塞住子线程。
线程其他用法(其他方法)
from threading import Thread,currentThread,enumerate,activeCount
# import threading
import time
# threading.current_thread()
# threading.current_thread()
def task():
print('子线程 start')
time.sleep(2)
print('子线程 end')
print(enumerate())
# print(currentThread(),'子线程')
if __name__ == '__main__':
t1 = Thread(target=task)
t2 = Thread(target=task)
t1.start()
t2.start()
# print(t1.is_alive()) # True
# print(t1.getName()) # Thread-1
# print(t2.getName()) # Thread-2
# t1.setName('班长')
# print(t1.getName())
# print(currentThread().name)
# print(enumerate()) # [<_MainThread(MainThread, started 1856)>, <Thread(Thread-1, started 6948)>, <Thread(Thread-2, started 3128)>]
# print(activeCount()) # 3
# print(len(enumerate())) # 3
is_alive():判断线程是活是死
get_Name():获得线程的名字
set_Name():修改线程的名字。
currentThread():返回当前线程的名称
enumerate():返回当前还存活着的线程,不包括未开启和已结束的,以列表的形式
activeCount():返回活着的线程的数量
守护线程
那么这个和守护进程也是很像的东西了
# 守护线程 守护的是进程的运行周期
from threading import Thread,enumerate,currentThread
import time
def task():
print('守护线程开始')
print(currentThread())
time.sleep(20)
# print('守护线程结束')
def task2():
print('子线程 start')
time.sleep(5)
print(enumerate())
print('子线程 end')
if __name__ == '__main__':
t1 = Thread(target=task)
t2 = Thread(target=task2)
t1.daemon = True
t2.start()
t1.start()
print('主')
一.线程与进程运行完毕的区别:
1.主进程运行完毕指的是主进程代码运行完毕
2.主线程运行完毕指的是所在的进程内的所有非守护线程运行完毕后,主线程才算运行完毕
强调:运行完毕,并非是终止
二.守护进程:主进程代码运行完毕,守护进程也就结束(守护的是主进程)
主进程要等非守护进程都运行完毕后再回收子进程的资源(否则会产生僵尸进程)才结束
主进程等子进程是因为主进程要给子进程收尸(代用wait方法向操作系统发起回收资 源信号(pid号,状态信息))
守护进程:主进程代码运行完毕,守护进程也就结束
守护线程:非守护线程运行完毕,守护线程结束
无论是进程还是线程,都遵循:守护xxx会等待主xxx运行完毕后被销毁
需要强调的是:运行完毕并非终止运行
#1.对主进程来说,运行完毕指的是主进程代码运行完毕
#2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕
详细解释:
#1 主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束,
#2 主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。