什么是线程
进程 : 资源分配的最小单位,可以利用多核,操作系统调度,数据不安全,开启关闭切换时间开销大
线程 :能被操作系统调度(给CPU执行)的最小单位.
打开一个程序就会产生进程 开启空间,加载数据. 进程 之间 数据隔离,是 资源分配的最小单位,
进程之内 会产生线程: 主要任务:执行代码. 数据互通
线程是依赖于进程, 一个进程可以包含多个线程.但是一定有一个主线程. 线程才是cpu执行的最小单元.
线程vs进程(理论)
- 开启多进程开销非常大. 10~100.开启线程开销非常小.
- 开启多进程的速度慢,开启多线程速度快.
- 进程之间数据不能直接共享通过队列可以.同一个进程下的线程之间的数据可以共享
线程创建2种方式
与创建 进程差不了多少
# 第一种方式 Thread父来的 线程
# from threading import Thread
# def task(name):
# print(f'{name} is running')
# if __name__ == '__main__':
# t = Thread(target=task,args=('mcsaoQ',))
# t.start()
# print('主线程')
from threading import Thread
class MyThread(Thread):
def run(self):
print(f'{self.name} is running')
if __name__ == '__main__':
t = MyThread()
t.start()
print('主线程')
线程与速度对比.
线程绝对要比进程要快:
先有进程才有线程 线程是cpu调度的最小单位 线程不用开空间
pid
在同一进程下 不同线程下 os.getpid()都是本线程的pid
# pid 进程号
from threading import Thread
import os
def task():
print(f'子线程: {os.getpid()}')
if __name__ == '__main__':
t = Thread(target=task,)
t.start()
print(f'主线程: {os.getpid()}')
线程之间共享资源
在同一进程下 数据是共享的
# from threading import Thread
# x = 1000
# def task():
# global x
# x = 0
# if __name__ == '__main__':
# t = Thread(target=task)
# t.start()
# # t.join()
# print(f'主线程: {x}')
线程的其他方法
# threading模块的方法:
# # print(threading.current_thread().name) # MainThread name 是当前线程的名字,
print(threading.current_thread().ident) 是当前线程的id
# # print(threading.enumerate()) # 返回一个列表 放置的是所有的线程对象
# print(threading.active_count()) # 获取活跃的线程的数量(包括主线程)
# from threading import Thread
# import threading
# import time
# def task(name):
# time.sleep(1)
# print(f'{name} is running')
# print(threading.current_thread().name)
#
# if __name__ == '__main__':
# for i in range(5):
# t = Thread(target=task,args=('mcsaoQ',))
# t.start()
# # 线程对象的方法:
# # time.sleep(1)
# # print(t.is_alive()) # 判断子线程是否存活 ***
# # print(t.getName()) # 获取线程名
# # t.setName('线程111')
# # print(t.getName()) # 获取线程名
多线程中的守护线程
# 主线程什么时候结束???
# 多线程是同一个空间,同一个进程,进程代表 空间,资源. 静态的.
# 主线程是进程空间存活在内存中的必要条件.
# 主线程: 必须要等待所有的子线程全部结束之后,你在执行完毕,进程在消失.
# 守护线程必须等待主线程结束才结束, 主线程必须等待所有的非守护线程结束才能结束.
推算出
# 守护线程: 必须等待所有的非守护线程 以及 主线程结束之后才能够结束.
互斥锁(锁)
# 互斥锁与join区别?
# 互斥锁 随机抢锁,公平. join 提前排好顺序,不公平.但是都是串行.
# from threading import Thread
# import time
# x = 100
#
# def task():
# global x
# temp = x
# time.sleep(1)
# temp -= 1
# x = temp
#
# if __name__ == '__main__':
# t = Thread(target=task)
# t.start()
# t.join()
# print(f'主线程{x}')
没加锁
from threading import Thread
import time
x = 100
def task():
global x
temp = x
time.sleep(1)
temp -= 1
x = temp
if __name__ == '__main__':
t_l = []
for i in range(100):
t = Thread(target=task)
t_l.append(t)
t.start()
for i in t_l:
i.join()
print(f'主线程{x}')
加了锁 并发
from threading import Thread
from threading import Lock
x = 100
def task(lock):
global x
lock.acquire()
temp = x
# time.sleep(0.1)
temp -= 1 # 99
x = temp # 99
lock.release()
if __name__ == '__main__':
lock = Lock()
t_l = []
for i in range(100):
t = Thread(target=task,args=(lock,))
t_l.append(t)
t.start()
for i in t_l:
i.join()
print(f'主线程{x}')
死锁现象,
第一种 加了2次同样的锁
第2种 2个进程都想获取对方的锁 却不可能实现
可重复锁Rlock
可重复锁,是线程相关的锁
不管实列化多少次都是同一只把锁,
引用计数,只要计数不为0,其他线程不可以抢.
from threading import Thread
from threading import RLock
import time
lock_A = lock_B = RLock()
class MyThread(Thread):
def run(self):
self.f1()
self.f2()
def f1(self):
lock_A.acquire()
print(f'{self.name}拿到 A锁')
lock_B.acquire() # 第二个线程抢B锁
print(f'{self.name}拿到 B锁')
lock_B.release()
lock_A.release()
def f2(self):
lock_B.acquire()
print(f'{self.name}拿到 B锁')
time.sleep(1) # 第一个线程 睡1秒
lock_A.acquire() # 第一个线程抢A锁
print(f'{self.name}拿到 A锁')
lock_A.release()
lock_B.release()
if __name__ == '__main__':
# for i in range(3):
# t = MyThread()
# t.start()
t1 = MyThread()
t1.start()
t2 = MyThread()
t2.start()
t3 = MyThread()
t3.start()
print('主线程')
信号量
本质就是一个计数器,用来为多个进程共享的数据结构提供受控访问。
from threading import Semaphore
也是一把锁
# 之前讲的锁都是只允许一个线程或者进程进入.
# 信号量允许多个线程或者进程同时进入
from threading import current_thread
from threading import Semaphore
import time
import random
sm = Semaphore(4)#控制进入个数
def go_public_wc():
sm.acquire()
print(f'{current_thread().name}进入了')
time.sleep(random.randint(1, 3))
sm.release()
if __name__ == '__main__':
for i in range(20):
t = Thread(target=go_public_wc)
t.start()
面试题
进程:正在执行的程序文件,它是资源单位
线程:cpu调度的最小单元,执行单位
多进程:同-时刻开启多个进程并发(并行)的执行.
多线程:同一时刻开启多个线程并发(并行)的执行.
守护线程:设置一个子线程为守护线程,它会等待所有的非守护线程以及主线程结束之后,在结束.
死锁现象?
多个线程(进程)竞争资源如果开启的互斥锁过多会遇到互相抢锁,等待的情况程序夯住.
同时给一个线程(进程)连续加锁多次.
利用递归锁解决死锁现象.
什么递归锁: Rlock锁上有引用计数每次引用计数+ 1,释放时计数-1,只要计数不为0,其余线程或者进
程都不可以引用此锁.
信号量:同一时刻允许多个线程(进程)开启任务.