多线程详解
线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包含在进行之中,是进程中的实际运作单位。线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属于一个进程的其他线程共享所拥有的全部资源。一个线程可以创建和撤销另外一个线程,同一个进程中的多个线程可以并发执行。
为什么要使用多线程
1、线程在程序中是独立的、并发的执行。与分隔的进程相比,进程中线程之间的隔离程度要小,它们共享内存、文件句柄和其他进程应有的状态。
2、线程的划分尺度小于进程,使得多线程程序的并发性高。进程在执行过程中拥有独立的内存单元,而多个线程中共享内存,从而极大提高了程序的运行效率。
3、线程比进程有更高的性能,由于同一个进程中线程都有共性,多个线程共享一个虚拟进程空间。线程的共享环境包括进程代码段、进程的共有数据,利用这些共享数据,线程之间很容易实现通信。
4、操作系统在创建进程时,必须为进程分配独立的内存空间,并分配大量的相关的资源,在创建线程时候就简单的多。
因此多线程来实现并发要比多进程的性能要高得多。
使用多线程的优点
1、进程之间不能共享内存,但线程之间共享内存非常容易。
2、操作系统创建进程时,需要为该进程重新分配资源,但创建线程代价很小。
3、python内置了多线程库,而不是单纯的作为底层操作系统的调度方式,简化了python多线程编程。
普通多线程创建
import time
import threading
def run(n):
print("task",n)
time.sleep(1)
print("2s")
time.sleep(1)
print("1s")
time.sleep(1)
print("0s")
time.sleep(1)
if __name__ == "__main__":
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
自定义线程
import time
import threading
class MyThread(threading.Thread):
def __init__(self,n):
super(MyThread,self).__init__()
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()
守护线程
守护线程,又称后台线程,它是在后台运行的,如果所有前台线程都死亡,那么后台线程就会自动死亡。
使用setDaemon(True)把所有的子线程都变成主线程的守护线程,因此当主线程结束后,子线程也会随之结束,整个程序就会退出。
线程守护就是主线程不管该线程的执行情况,只要是其他子线程结束且主线程执行完毕,主线程都会关闭。主线程不等待该守护线程执行完在关闭。
import time
import threading
def run(n):
print("task",n)
time.sleep(1)
print("2s")
time.sleep(1)
print("1s")
time.sleep(1)
print("0s")
time.sleep(1)
if __name__ == "__main__":
t = threading.Thread(target=run,args=('t1',))
t.setDaemon(True)
t.start()
t.join()
print('end')
task t1
end
通过执行的结果可以看出,设置守护线程之后,当主线程结束后,子线程立即结束不再执行。
多线程共享全局变量
import time
import threading
def work1():
global g_num
for i in range(3):
g_num += 1
print('in work1 g_num is: %d' % g_num)
def work2():
global g_num
print('in work2 g_numis %d:' % g_num)
if __name__ == "__main__":
t1 = threading.Thread(target=work1)
t1.start()
time.sleep(1)
t2 = threading.Thread(target=work2)
t2.start()
由于线程之间是进行随即调度,并且每个线程可能只能执行n条之后,当多个线程同时修改同一条数据时可能出现脏数据,所以出现了线程锁,同一时刻只允许一个线程执行操作。
线程锁用于锁定资源,可以定义多个锁,当需要独占某一资源时,任何一个锁都可以锁定这个资源。
由于线程之间是进行随即调度的,如果有多个线程同时操作一个对象,如果没有很好的保护可能会造成不可预期的结果。
import time
import threading
import os
from threading import Lock,Thread
def work():
global n
lock.acquire()
temp = n
time.sleep(0.1)
n = temp - 1
lock.release()
if __name__ == "__main__":
lock = Lock()
n = 100
l = []
for i in range(100):
p = Thread(target=work)
l.append(p)
p.start()
for p in l:
p.join()
信号量
互斥锁同时只允许一个线程更改数据,而semaphore同时允许一定数量的线程更改数据。
import time
import threading
import os
from threading import Lock,Thread
def run1(n,semaphore):
semaphore.acquire()
time.sleep(3)
print('run the thread: %s \n'%n)
semaphore.release()
if __name__ == "__main__":
num = 0
semaphore = threading.BoundedSemaphore(5)
for i in range(22):
t = threading.Thread(target=run1,args=('t-%s'%i,semaphore))
t.start()
while threading.active_count() != 1:
pass
else:
print('--------all threads done---------')