线程
多线程 1
1. 什么是线程 3
2. 为何用线程 3
3. 开启线程的两种方式 3
3.1. 方式一(引用系统Thread类) 3
3.2. 方式二(自定义类) 4
4. 线程VS进程 4
4.1. 内存共享的隔离 4
4.1.1. 代码演示 4
4.2. 创建速度 5
4.2.1. 代码演示 5
5. 什么时候用线程 5
6. 守护线程 5
6.1. 代码演示 5
7. 互斥锁 6
7.1. 代码演示 7
8. 一些了解方法 8
8.1. 代码演示 8
9. 死锁现象和递归锁 8
9.1. 死锁现象代码演示 8
9.2. 递归锁 9
10. 信号量 10
10.1. 代码演示 10
1. 什么是线程
进程其实不是一个执行单位,而是一个资源单位,每个进程内自带一个线程,线程才是cpu的执行单位。如果把操作系统比喻成一座工厂,那么线程相当于车间,而线程相当于车间里的流水线,并且该流水线不只一条。
2. 为何用线程
我们可以把python运行想象成一个进程,它会向内存申请一个内存空间,而python中的代码则是一个线程,代码会自上而下的运行,并且这个运行时基于内存空间的,代码的运行是由cpu执行的,因为cpu执行的时代码(某种计算)而不是内存资源。
进程与线程都是一个抽象概念,通过这个抽象概念可以更简洁的来描述一个过程。
3. 开启线程的两种方式
运行代码的过程中就已经造出一条线程,它是主线程。
不论多少个子线程都是子线程,都是主线程下的分支。
线程可以不用main,因为线程之间共享同一个进程的内存空间。
3.1. 方式一(引用系统Thread类)
from threading import Thread
import time
def task(name):
print("%s running" % name)
time.sleep(1)
print("%s end" % name)
if __name__ == '__main__':
t = Thread(target=task, args=('subthread',))
t.start()
print("parent")
3.2. 方式二(自定义类)
from threading import Thread
import time
class MyThread(Thread):
def run(self):
print("%s running" % self.name)
print("%s end" % self.name)
if __name__ == '__main__':
t1 = MyThread()
t2 = MyThread()
t3 = MyThread()
t4 = MyThread()
t5 = MyThread()
t6 = MyThread()
t6.start()
t5.start()
t4.start()
t3.start()
t1.start()
t2.start()
print("parent")
4. 线程VS进程
进程:资源的申请与销毁
线程:单指代码的执行过程
4.1. 内存共享的隔离
进程:多进程内存空间彼此隔离
线程:同进程内的多线程共享进程内存
4.1.1. 代码演示
from threading import Thread
x = 100
def task():
global x
x=0
if __name__ == '__main__':
t = Thread(target=task) # 在parent打印前线程运行
t.start()
print("parent",x) # x输出为0
4.2. 创建速度
造线程的速度远远快于造进程的速度,因为造进程需要在内存中申请一个内存空间,而造线程只需要向操作系统发送请求让他完成某个任务,拿个通俗的比喻则是造车间耗费的时间一定慢于造一个流水线的时间。
4.2.1. 代码演示
from threading import Thread
from multiprocessing import Process
import time
def task(name):
print("%s running"%name)
time.sleep(1)
print("%s end"%name)
if __name__ == '__main__':
t = Thread(target=task,args=('subthread',)) # 在parent打印前线程运行
p = Process(target=task,args=('subprocess',)) # 在paren打印后运行
p.start()
t.start()
print("parent")
5. 什么时候用线程
我们现在想象一下文本编辑器的工作原理:首先写入文件、其次取出来往硬盘打印、最后实现一个定期从内存往硬盘刷入的功能。
这个时候如果三个功能使用三个进程,由于数据在进程间传来传去会变得很麻烦;如果三个功能使用三个线程,数据是共用的,这时实现这个功能会很方便。
6. 守护线程
守护线程会在该进程内所有非守护线程结束才跟着结束,即守护整个进程的运行周期(所有的非守护线程都运行结束)
6.1. 代码演示
# from threading import Thread,current_thread
# import time
#
#
# def task():
# print('%s is running' % current_thread().name)
# time.sleep(3)
# print('%s is done' % current_thread().name)
#
#
# if __name__ == '__main__':
# t = Thread(target=task,name='守护线程')
# t.daemon=True
# t.start()
# print('主')
from threading import Thread
import time
def foo():
print(123)
time.sleep(3)
print("end123")
def bar():
print(456)
time.sleep(1)
print("end456")
t1=Thread(target=foo)
t2=Thread(target=bar)
t1.daemon=True
t1.start()
t2.start()
print("main-------")
'''
123
456
main-------
end456
'''
7. 互斥锁
线程运行速度太快,会影响测试结果,所以一般情况会认为自带互斥锁效果,其实不然,同进程一样,线程也需要互斥锁来保护数据的安全性,同时在这个过程中也牺牲了效率。
如果不使用join()会先打印main下面的print(x),也就造成了输出为100的打印结果(*****)
from threading import Thread, Lock
import time
x = 100
mutex = Lock()
def task():
global x
temp = x
time.sleep(1)
x = temp - 1
if __name__ == '__main__':
t = Thread(target=task)
t.start()
print(x)
7.1. 代码演示
# 速度运行过快时会造成数据传输安全的假象,因此给他放缓速度
from threading import Thread, Lock
import time
x = 100
mutex = Lock()
def task():
global x
temp = x
time.sleep(1)
temp -= 1
x = temp
if __name__ == '__main__':
tlst = []
for i in range(100):
t = Thread(target=task)
tlst.append(t)
t.start()
for i in range(len(tlst)):
tlst[i].join()
print(x)
# 加互斥锁版本
from threading import Thread, Lock
x = 100
mutex = Lock()
def task():
global x
with mutex:
x -= 1
if __name__ == '__main__':
for i in range(100):
t = Thread(target=task)
t.start()
print(x)
8. 一些了解方法
主要有:isAlive,getName,setName
导入模块的:active_count, enmurate,current_thread
8.1. 代码演示
from threading import Thread, current_thread, active_count, enumerate
import time
def task():
print("%s 我是子线程的原名" % current_thread().name)
current_thread().setName("子线程")
time.sleep(1)
print("%s 我是修改后的子线程名" % current_thread().name)
if __name__ == '__main__':
t = Thread(target=task)
t.start()
print("我能判断线程是否存活", t.is_alive())
print("我能把所有线程放入列表", enumerate())
print("我能显示存活的线程数", active_count())
print("这是我主线程的原名", current_thread().getName())
current_thread().setName("主线程") # 修改主线程的名字
print("parent", current_thread().name, "我原名MainThread,现名主线程")
9. 死锁现象和递归锁
9.1. 死锁现象代码演示
# 创建两把锁,创建多个线程,第一个线程拿着B锁等着线程二释放A锁, 第二个线程拿着A锁等着线程一释放B锁,总而言之是两个线程各自拿着对方需要的锁。
from threading import Thread,Lock,active_count
import time
mutexA = Lock()
mutexB = Lock()
class MyThread(Thread):
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print("A")
mutexB.acquire()
print("B")
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print("A")
time.sleep(1)
mutexA.acquire()
print("B")
mutexA.release()
mutexB.release()
if __name__ == '__main__':
for i in range(10):
t = MyThread()
t.start()
print(active_count())
9.2. 递归锁
from threading import Thread, active_count, RLock
import time
obj = RLock()
mutexA = obj
mutexB = obj
class MyThread(Thread):
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print("A")
mutexB.acquire()
print("B")
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print("A")
time.sleep(1)
mutexA.acquire()
print("B")
mutexA.release()
mutexB.release()
if __name__ == '__main__':
for i in range(10):
t = MyThread()
t.start()
print(active_count())
10. 信号量
控制同一时间互斥锁的量,并且有人释放锁会有人马上拿到锁
10.1. 代码演示
from threading import Thread, Semaphore, current_thread
import time
import random
sm = Semaphore(5)
def task():
with sm:
print("%s正在舒服" % current_thread().name)
time.sleep(random.random())
if __name__ == '__main__':
for i in range(20):
t = Thread(target=task)
t.start()

浙公网安备 33010602011771号