并发编程之线程
1、什么是线程
线程指的是一条流水线的工作过程
进程根本就不是一个执行单位,进程其实是一个资源单位
一个进程内自带一个线程,线程才是执行单位
2、进程VS线程
1、同一进程内的线程们共享该进程内资源,不同进程内的线程资源肯定是隔离的
2、创建线程的开销比创建进程要小的多
3、开启线程的两种方式:
导入Therad模块,该模块的接口与multiprocess模块功能相同。
方式一:
from multiprocessing import Process from threading import Thread import time def task(name): print('%s is running' %name) time.sleep(3) if __name__ == '__main__': t=Thread(target=task,args=('egon',)) # t=Process(target=task,args=('egon',)) t.start() print('主线程')
方式二:
from multiprocessing import Process from threading import Thread import time class MyThread(Thread): def run(self): print('%s is running' %self.name) time.sleep(3) if __name__ == '__main__': t=MyThread() t.start() print('主线程')
多线程与多进程的区别:
在主进程下开启多个线程,每个线程都跟主进程的pid一样,说明同一进程下,多线程使用的资源是相同的。
from threading import Thread import time,os def task(): print('%s is running' %os.getpid()) time.sleep(3) if __name__ == '__main__': t=Thread(target=task,) t.start() print('主线程',os.getpid())
同一进程内的线程之间共享进程内的数据
from threading import Thread import time,os x=1000 def task(): global x x=0 if __name__ == '__main__': t=Thread(target=task,) t.start() t.join() print('主线程',x)
守护线程:
无论是进程还是线程都遵循守护原则。
两者的区别在于:1、主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源,才会结束。
2、主线程在其它非守护线程运行完毕后才算运行完毕(收护线程在此时就被回收)。因为主进程的结束意味者进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。
from threading import Thread import time def foo(): print(123) time.sleep(5) print("end123") def bar(): print(456) time.sleep(3) print("end456") if __name__ == '__main__': t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True #定义t1为守护线程,t1线程会等主线程代码执行完毕就结束 t1.start() t2.start() print("main-------") #主线程会等非守护线程代码执行完毕就回收资源 #打印结果 ''' 123 456 main------- end456 '''
多线程互斥锁:
线程的互斥锁与进程的互斥锁的功能,使用方法都一样,都是将多线程的并发变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。
from threading import Thread,Lock import time mutex=Lock() x=100 def task(): global x mutex.acquire() #只对要修改的数据加锁 temp=x time.sleep(0.1) x=temp-1 mutex.release() if __name__ == '__main__': start=time.time() t_l=[] for i in range(100): t=Thread(target=task) t_l.append(t) t.start() for t in t_l: t.join() print('主',x) print(time.time()-start)
死锁现象与递归锁:
死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁。
from threading import Thread,Lock import time mutexA=Lock() mutexB=Lock() class MyThread(Thread): def run(self): self.func1() self.func2() def func1(self): mutexA.acquire() print('\033[41m%s 拿到A锁\033[0m' %self.name) mutexB.acquire() print('\033[42m%s 拿到B锁\033[0m' %self.name) mutexB.release() mutexA.release() def func2(self): mutexB.acquire() print('\033[43m%s 拿到B锁\033[0m' %self.name) time.sleep(2) mutexA.acquire() print('\033[44m%s 拿到A锁\033[0m' %self.name) mutexA.release() mutexB.release() if __name__ == '__main__': for i in range(10): t=MyThread() t.start() ''' Thread-1 拿到A锁 Thread-1 拿到B锁 Thread-1 拿到B锁 Thread-2 拿到A锁 然后就卡住,死锁了 '''
解决方法,递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:
from threading import Thread,Lock,RLock
import time
mutexA=mutexB=RLock() class MyThread(Thread): def run(self): self.f1() self.f2() def f1(self): mutexA.acquire() print('%s 拿到了A锁' %self.name) mutexB.acquire() print('%s 拿到了B锁' %self.name) mutexB.release() mutexA.release() def f2(self): mutexB.acquire() print('%s 拿到了B锁' %self.name) time.sleep(0.1) mutexA.acquire() print('%s 拿到了A锁' %self.name) mutexA.release() mutexB.release() if __name__ == '__main__': for i in range(10): t=MyThread() t.start() # t1=MyThread() # t1.start() # # t2=MyThread() # t2.start() # # t3=MyThread() # t3.start() print('主')
mutexA=mutexB=threading.RLock() #一个线程拿到锁,counter加1,该线程内又碰到加锁的情况,则counter继续加1,这期间所有其他线程都只能等待,等待该线程释放所有锁,即counter递减到0为止
递归锁可以连续acquire多次,互斥锁只能acquire一次。
信号量:
信号量也是一把锁,但是可以指定钥匙数量,信号量同一时间可以有几个任务拿到锁去执行。
from threading import Thread,Semaphore,current_thread import time,random sm=Semaphore(5) #规定同一时间可以有5个子线程执行任务 def go_wc(): sm.acquire() print('%s 上厕所ing' %current_thread().getName()) time.sleep(random.randint(1,3)) sm.release() if __name__ == '__main__': for i in range(23): t=Thread(target=go_wc) t.start()
Semaphore管理一个内置的计数器, 每当调用acquire()时内置计数器-1; 调用release() 时内置计数器+1; 计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。