多线程
- 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
- 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线 3.进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)
- 及一些进程级的资源(如打开文件和信号等),某进程内的线程在其他进程不可见;
- 调度和切换:线程上下文切换比进程上下文切换要快得多
多线程类似于同时执行多个不同程序,多线程运行有如下优点:
- 使用线程可以把占据长时间的程序中的任务放到后台去处理。
- 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
- 程序的运行速度可能加快
- 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
多线程的三种基本状态
线程有 就绪、阻塞、运行 三种基本状态。
- 就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;
- 运行状态是指线程占有处理机正在运行;
- 阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。
三种状态的相互转化如下图所示:
创建多线程
import threading
import time
def test(x):
print(x)
time.sleep(2)
class MyThread(threading.Thread):
def __init__(self, n):
super(MyThread, self).__init__()
self.n = n
def run(self):
print("以类的方式创建多线程", self.n)
time.sleep(2)
r1 = MyThread(11)
r2 = MyThread(22)
r1.start()
r2.start()
多线程的特性
"""
多线程的特性
"""
import threading
import time
def run(x):
print(f"线程{x}\n")
time.sleep(2)
if __name__ == '__main__':
start_time = time.time()
res = []
for i in range(50):
t = threading.Thread(target=run, args=(i, ))
t.setDaemon(True)
t.start()
res.append(t)
print(f"线程个数{threading.active_count()}")
print("两个run函数运行时间", time.time() - start_time)
print(threading.current_thread())
线程锁
锁(Lock):一旦线程获得锁,其他试图获取锁的线程将被阻塞等待。 锁:凡是存在共享支援争抢的地方都可以使用锁,从而保证只有一个使用者可以完全使用这个资源。
名称 | 含义 |
---|
Lock.acquire(blocking=True,timeout=-1) | 获取锁,获取成功返回True,否则返回False当获取不到锁时,默认进入阻塞状态,直到获取到锁,后才继续。阻塞可以设置超时时间。非阻塞时,timeout禁止设置。如果超时依旧未获取到锁,返回False。 | Lock.rease() | 释放锁,可以从任何线程调用释放。已上锁的锁,会被设置为unlocked。如果未上锁调用,会抛出RuntimeError异常。 |
加锁,解锁 一般来说,加锁就需要解锁,但是加锁后解锁前,还要有一些代码执行,就有可能抛异常,一旦出现异常,锁是无 法释放,但是当前线程可能因为这个异常被终止了,这也产生了死锁
加锁、解锁常用语句:
- 使用try…?nally语句保证锁的释放
- with上下文管理,锁对象支持上下文管理
锁的应用场景:
- 锁适用于访问和修改同一个共享资源的时候,即读写同一个资源的时候。
使用锁注意事项:
- 少用锁,必要时用锁。使用了锁,多线程访问被锁的资源时,就成了串行,要么排队执行,要么争抢执行。举例,高速公路上车并行跑,可是到了省界只开放了一个收费口,过了这个口,车辆依然可以在多车道 上一起跑。过收费口的时候,如果排队一辆辆过,加不加锁一样效率相当,但是一旦出现争抢,就必须 加锁一辆辆过。注意,不管加不加锁,只要是一辆辆过,效率就下降了。
- 加锁时间越短越好,不需要就立即释放锁
- 一定要避免死锁
"""
线程锁
"""
import threading
lock = threading.Lock()
def run():
global x
lock.acquire()
x += 1
lock.release()
if __name__ == '__main__':
x = 0
res = []
for i in range(100):
t = threading.Thread(target=run)
t.start()
res.append(t)
for t in res:
t.join()
print(x)
递归锁
可重入锁,是线程相关的锁。 线程A获得可重复锁,并可以多次成功获取,不会阻塞。最后要在线程A中做和acquire次数相同的release。
"""
递归锁
"""
import threading
def run1():
global x
lock.acquire()
x += 1
lock.release()
return x
def run2():
global y
lock.acquire()
y += 1
lock.release()
return y
def run3():
lock.acquire()
res1 = run1()
res2 = run2()
lock.release()
print(res1, res2)
if __name__ == '__main__':
x = 0
y = 0
lock = threading.RLock()
for i in range(50):
t = threading.Thread(target=run3)
t.start()
while threading.active_count() != 1:
print(f'正在运行{threading.active_count()}个线程')
print("程序运行结束")
作业
在一个线程中,每秒循环输出当前的年月日时分秒;于此同时, 在另一个线程中,实现张三的姓名每2秒打印输出4次结束。注意:都需要使用类和继承实现功能。
import threading
import time
class TwoThread(threading.Thread):
def __init__(self, type):
super().__init__()
self.type = type
def run(self):
if self.type == 1:
self.cur_time()
else:
self.get_Name()
def cur_time(self):
while True:
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
time.sleep(1)
def get_Name(self):
for i in range(4):
print("姓名:张三")
time.sleep(2)
if __name__ == '__main__':
tt1 = TwoThread(1)
tt2 = TwoThread(2)
tt1.start()
tt2.start()
|