Day 28 线程
---恢复内容开始---
threading模块
线程的创建:
第一种方式:
from threading import Thread import time def work(): print('hello') time.sleep(0.5) print('word') if __name__ == '__main__': t = Thread(target=work) t.start() t.join() print('主线程')
第二种方式: 通过类的继承
from threading import Thread
import time
class Work(Thread):
def __init__(self):
super().__init__()
def run(self):
print('hello')
time.sleep(0.5)
print('word')
if __name__ == '__main__':
t = Work()
t.start()
t.join()
print('主线程')
多进程与多线程的比较:
from threading import Thread import os from multiprocessing import Process def work(): print('hello',os.getpid()) if __name__ == '__main__': # part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样 t1 = Thread(target=work) t2 = Thread(target=work) t1.start() t2.start() print('主线程',os.getpid()) # part2:开多个进程,每个进程都有不同的pid p1 = Process(target=work) p2 = Process(target=work) p1.start() p2.start() print('主线程', os.getpid())
---恢复内容结束---
threading模块
线程的创建:
第一种方式:
from threading import Thread import time def work(): print('hello') time.sleep(0.5) print('word') if __name__ == '__main__': t = Thread(target=work) t.start() t.join() print('主线程')
第二种方式: 通过类的继承
from threading import Thread
import time
class Work(Thread):
def __init__(self):
super().__init__()
def run(self):
print('hello')
time.sleep(0.5)
print('word')
if __name__ == '__main__':
t = Work()
t.start()
t.join()
print('主线程')
多进程与多线程的比较:
from threading import Thread import os from multiprocessing import Process def work(): print('hello',os.getpid()) if __name__ == '__main__': # part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样 t1 = Thread(target=work) t2 = Thread(target=work) t1.start() t2.start() print('主线程',os.getpid()) # part2:开多个进程,每个进程都有不同的pid p1 = Process(target=work) p2 = Process(target=work) p1.start() p2.start() print('主线程', os.getpid())
from threading import Thread from multiprocessing import Process import os def work(): print('hello') if __name__ == '__main__': #在主进程下开启线程 t=Thread(target=work) t.start() print('主线程/主进程') ''' 打印结果: hello 主线程/主进程 ''' #在主进程下开启子进程 t=Process(target=work) t.start() print('主线程/主进程') ''' 打印结果: 主线程/主进程 hello '''
from threading import Thread from multiprocessing import Process import os def work(): global n n=0 if __name__ == '__main__': # n=100 # p=Process(target=work) # p.start() # p.join() # print('主',n) #毫无疑问子进程p已经将自己的全局的n改成了0,但改的仅仅是它自己的,查看父进程的n仍然为100 n=1 t=Thread(target=work) t.start() t.join() print('主',n) #查看结果为0,因为同一进程内的线程之间共享进程内的数据
Thread 类的其他方法:
Thread实例对象的方法 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
from threading import Thread import time def sayhi(name): time.sleep(2) print('%s say hello' %name) if __name__ == '__main__': t=Thread(target=sayhi,args=('egon',)) t.start() t.join() print('主线程') print(t.is_alive()) ''' egon say hello 主线程 False '''
守护线程:
无论是进程还是线程,都遵循:守护xx会等待主xx运行完毕后被销毁。需要强调的是:运行完毕并非终止运行
#1.对主进程来说,运行完毕指的是主进程代码运行完毕 #2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕,也就是守护线程是守护所有的主线程和子线程
#1 主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束, #2 主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。
import time from threading import Thread def func(): while True: print('in func') time.sleep(0.5) def func2(): print('开始') time.sleep(5) print('结束') t = Thread(target=func) Thread(target=func2).start() t.setDaemon(True) t.start() print('主线程') time.sleep(3) print('主线程结束')
锁:
# 互斥锁:
1.无论是相同的线程还是不同的线程,都只能连续acquire一次
2.要想再acquire,必须线release
# 递归锁:
1.再同一个线程中,可以无限次acquire
2.要想在其他线程中acquire,必须在自己的线程中添加和acquire次数相同的release
为什么有了GIL,还要锁,因为GIL还是有可能出现数据不安全的情况
出现数据不安全的两个条件:
#1.操作全局变量
#2.出现-= +=这样的操作
同步锁:
from threading import Thread import os,time def work(): global n temp=n time.sleep(0.1) n=temp-1 if __name__ == '__main__': n=100 l=[] for i in range(100): p=Thread(target=work) l.append(p) p.start() for p in l: p.join() print(n)
from threading import Thread,Lock import os,time def work(lock): global n with lock: temp=n time.sleep(0.001) n=temp-1 if __name__ == '__main__': n=100 l=[] lock = Lock() for i in range(100): p=Thread(target=work, args=(lock,)) l.append(p) p.start() for p in l: p.join() print(n)
互斥锁与join的区别:
#有的同学可能有疑问:既然加锁会让运行变成串行,那么我在start之后立即使用join,就不用加锁了啊,也是串行的效果啊 #没错:在start之后立刻使用jion,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是 #start后立即join:任务内的所有代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的 #单从保证数据安全方面,二者都可以实现,但很明显是加锁的效率更高.
死锁与递归:
所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁
from threading import Thread,Lock import os,time noodle_lock = Lock() frok_lock = Lock() def fun1(name): frok_lock.acquire() print('%s拿到了叉子'%name) noodle_lock.acquire() print('%s拿到了面条'%name) print('%s开始吃面了'%name) time.sleep(0.1) frok_lock.release() print('%s放下叉子'%name) noodle_lock.release() print('%s放下了面条'%name) def fun2(name): noodle_lock.acquire() print('%s拿到了面条'%name) frok_lock.acquire() print('%s拿到了叉子'%name) print('%s开始吃面了'%name) time.sleep(0.1) noodle_lock.release() print('%s放下了面条'%name) frok_lock.release() print('%s放下叉子'%name) if __name__ == '__main__': Thread(target=fun1,args=('alex',)).start() Thread(target=fun2,args=('addit',)).start() Thread(target=fun1,args=('wusir',)).start() Thread(target=fun2,args=('太白',)).start()
解决方法,递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:

浙公网安备 33010602011771号