一、线程背景 (详情参考:https://www.cnblogs.com/clschao/articles/9684694.html)
1、进程
之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。
进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。
2、为什么要有线程
进程的不足:
(1)进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
(2)进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。
3、注意:(1)进程是资源分配的最小单位,线程是CPU调度的最小单位.
在这里我们简单总结一下:
进程是最小的内存分配单位
线程是操作系统调度的最小党委
线程被CPU执行了
进程内至少含有一个线程
进程中可以开启多个线程
开启一个线程所需要的时间要远小于开启一个进程
多个线程内部有自己的数据栈,数据不共享
全局变量在多个线程之间是共享的
from threading import Thread def fun(n): print(n) if __name__ == '__main__': t=Thread(target=fun,args=(123,)) t.start() print("主线程结束")
class MyThread(Thread): def __init__(self,n): super(MyThread, self).__init__() self.n=n def run(self): print(self.n) print("xxxxxxxxxxxxx") if __name__ == '__main__': t=MyThread(456) t.start() print("父线程jieshu")
2、线程 t.join() :等待子线程执行完后才会继续执行主线程的代码
import time from threading import Thread def func(): time.sleep(1) print('我是子线程') if __name__ == '__main__': t = Thread(target=func,) t.start() print('开始等待子线程了') t.join() #阻塞,等待子线程执行完毕,才继续执行 print('主线程结束')
3、线程的其他方法
Thread实例对象的方法 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果
import time from threading import Thread from threading import current_thread import threading def func(): time.sleep(3) # current_thread().ident print('我是子线程,名字是',current_thread().getName()) print('我是子线程,id是',current_thread().ident) if __name__ == '__main__': for i in range(10): t = Thread(target=func,) t.start() # print(t.isAlive()) print(threading.enumerate()) print(threading.activeCount()) print('主线程结束')
4、进程与线程效率对比
import time from threading import Thread from multiprocessing import Process def func(): # time.sleep(3) print('xxxx') if __name__ == '__main__': t_list = [] t_s_t = time.time() #线程执行起始时间 for i in range(100): t = Thread(target=func,) t_list.append(t) t.start() [tt.join() for tt in t_list] t_e_t = time.time() #线程执行结束时间 t_dif_t = t_e_t - t_s_t #线程执行所需时间 p_list = [] p_s_t = time.time() #进程执行起始时间 for i in range(100): p = Process(target=func,) p_list.append(p) p.start() [pp.join() for pp in p_list] p_e_t = time.time() #进程执行结束时间 p_dif_t = p_e_t - p_s_t #进程执行所需时间 print('多线程的时间>>>',t_dif_t) print('多进程的时间>>>',p_dif_t) print('主线程结束')
5、数据共享信息安全问题验证
结果:不可靠。会造成数据混乱。
import time from threading import Thread num = 100 def func(): # time.sleep(0.01) global num num -= 1 if __name__ == '__main__': # for i in range() t = Thread(target=func,) t.start() t.join() print('主线程的num',num)
import time from threading import Thread num = 100 def func(): # time.sleep(3) global num tep = num time.sleep(0.001) tep = tep - 1 num = tep # num -= 1 if __name__ == '__main__': t_list = [] for i in range(100): t = Thread(target=func,) t_list.append(t) t.start() [tt.join() for tt in t_list] # t.join() print('主线程的num',num)
import time from threading import Thread import os def func(): time.sleep(1) print('我是子线程,我的pid是',os.getpid()) if __name__ == '__main__': t = Thread(target=func,) t.start() print('开始等待子线程了') print('主线程的pid',os.getpid()) t.join() print('主线程结束')
7、加锁解决数据不安全问题 (牺牲了效率,保证了数据安全)
import time from threading import Thread,Lock num = 100 def func(tl): # time.sleep(3) print('xxxxx') time.sleep(1) global num tl.acquire() #上锁 tep = num time.sleep(0.001) tep = tep - 1 num = tep tl.release() #释放锁 # num -= 1 if __name__ == '__main__': tl = Lock() t_list = [] for i in range(10): t = Thread(target=func,args=(tl,)) t_list.append(t) t.start() [tt.join() for tt in t_list] # t.join() print('主线程的num',num)
8、死锁现象
现象描述:双方互相等待对方释放对方手里锁,然后拿到的那个锁
所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
import time from threading import Thread,Lock,RLock def func1(lock_A,lock_B): lock_A.acquire() time.sleep(0.5) print('alex拿到了A锁') lock_B.acquire() print('alex拿到了B锁') lock_B.release() lock_A.release() def func2(lock_A,lock_B): lock_B.acquire() print('taibai拿到了B锁') lock_A.acquire() print('taibai 拿到了A锁') lock_A.release() lock_B.release() if __name__ == '__main__': lock_A = Lock() lock_B = Lock() t1 = Thread(target=func1,args=(lock_A,lock_B)) t2 = Thread(target=func2,args=(lock_A,lock_B)) t1.start() t2.start()
死锁现象: 引入递归锁RLock(lock_A=lock_B=RLock())
递归锁RLock自带计数器,遇到acquire计数器+1;遇到release,计数器-1,递归锁被一个对象夺走后就不能被其他对象获得,只有等释放为0时候才会开锁,让其他的进程抢夺。
import time from threading import Thread,Lock,RLock def func1(lock_A,lock_B): lock_A.acquire() time.sleep(0.5) print('alex拿到了B锁') lock_B.release() lock_A.release() def func2(lock_A,lock_B): lock_B.acquire() print('taibai拿到了B锁') lock_A.acquire() print('taibai 拿到了A锁') lock_A.acquire() print('taibai 拿到了A锁') lock_A.release() lock_B.release() if __name__ == '__main__': lock_A = lock_B = RLock() #递归深度不限,只有当递归锁计数器为0,才会释放给其他线程用 print(id(lock_A)) #具有相同内存地址 print(id(lock_B)) #具有相同的内存地址 t1 = Thread(target=func1,args=(lock_A,lock_B)) t2 = Thread(target=func2,args=(lock_A,lock_B)) t1.start() t2.start()
典型问题:科学家吃面,只有一碗面,一把叉子
import time from threading import Thread,Lock noodle_lock = Lock() fork_lock = Lock() def eat1(name): noodle_lock.acquire() print('%s 抢到了面条'%name) fork_lock.acquire() print('%s 抢到了叉子'%name) print('%s 吃面'%name) fork_lock.release() noodle_lock.release() def eat2(name): fork_lock.acquire() print('%s 抢到了叉子' % name) time.sleep(1) noodle_lock.acquire() print('%s 抢到了面条' % name) print('%s 吃面' % name) noodle_lock.release() fork_lock.release() for name in ['taibai','egon','wulaoban']: t1 = Thread(target=eat1,args=(name,)) t2 = Thread(target=eat2,args=(name,)) t1.start() t2.start()
9、守护线程(守护进程必须在start之前)
无论是进程还是线程,都遵循:守护xx会等待主xx运行完毕,如果主xx时间够长,守护xx执行完毕后被销毁,如果主xx时间较短的话,守护xx会终并销毁,如果主。需要强调的是:运行完毕并非终止运行。
#1.对主进程来说,运行完毕指的是主进程代码运行完毕 #2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕
import time from threading import Thread from multiprocessing import Process def func1(): time.sleep(3) print('任务1结束') def func2(): time.sleep(2) print('任务2结束') if __name__ == '__main__': t1 = Thread(target=func1,) t2 = Thread(target=func2,) # t1.daemon = True # t2.setDaemon(True) #将t1设置为守护线程 t1.setDaemon(True) #将t1设置为守护线程 t1.start() t2.start() print('主线程结束') #结果显示 主线程结束 任务2 #结果分析:t1被设置为守护进程,他线程时间为3s,t2线程的时间是2s, 当t2线程执行完后,结束守护进程,主线程代码就会执行到print(”主线程“),
# 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=('taibai',)) # t.setDaemon(True) # 必须在t.start()之前设置 # t.start() # # print('主线程') # print(t.is_alive()) # ''' # 主线程 # True # ''' from threading import Thread from multiprocessing import Process import time def func1(): while True: print(666) time.sleep(0.5) def func2(): print('hello') time.sleep(3) if __name__ == '__main__': # t = Thread(target=func1,) # t.daemon = True #主线程结束,守护线程随之结束 # # t.setDaemon(True) #两种方式,和上面设置守护线程是一样的 # t.start() # t2 = Thread(target=func2,) #这个子线程要执行3秒,主线程的代码虽然执行完了,但是一直等着子线程的任务执行完毕,主线程才算完毕,因为通过结果你会发现我主线程虽然代码执行完毕了, # 但是主线程的的守护线程t1还在执行,说明什么,说明我的主线程还没有完毕,只不过是代码执行完了,一直等着子线程t2执行完毕,我主线程的守护线程才停止,说明子线程执行完毕之后,我的主线程才执行完毕 # t2.start() # print('主线程代码执行完啦!') p = Process(target=func1, ) p.daemon = True p.start() p2 = Process(target=func2, ) p2.start() time.sleep(1) # 让主进程等1秒,为了能看到func1的打印效果 print('主进程代码执行完啦!') # 通过结果你会发现,如果主进程的代码运行完毕了,那么主进程就结束了,因为主进程的守护进程p随着主进程的代码结束而结束了,守护进程被回收了,这和线程是不一样的,主线程的代码完了并不代表主线程运行完毕了,需要等着所有其他的非守护的子线程执行完毕才算完毕
四、信号量
同进程的一样
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
实例:(同时只有5个线程可以获得semaphore,即可以限制最大连接数为5):
import time import random from threading import Thread,Semaphore def func1(i,s): s.acquire() # time.sleep(1) print('客官%s里边请~~'%i) time.sleep(random.randint(1, 3)) s.release() if __name__ == '__main__': s = Semaphore(4) for i in range(10): t = Thread(target=func1,args=(i,s)) t.start()
五、事件(Event)
事件的基本方法:
event.isSet():返回event的状态值; event.wait():如果 event.isSet()==False将阻塞线程; event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度; event.clear():恢复event的状态值为False。