第一章 线程的使用
并发:指的是任务数多余cpu核数
并行:指的是任务数小于等于cpu核数,即任务真的是一起执行的
1.线程的概念
线程就是在程序运行过程中,执行程序代码的一个分支,每个运行的程序至少都有一个线程。
1.进程至少有一个线程叫主线程
2.主线程要等其他线程执行完再结束
2.单线程的执行
必须一个个的执行
3.多线程
1.导入线程模块
import threading
from threading import Thread
2.线程类的Thread参数说明
group: 线程组,目前只能使用None
target: 执行的目标任务名
args: 以元组的方式给执行任务传参
kwargs: 以字典方式给执行任务传参
name: 线程名,一般不用设置
4.创建线程
创建线程,线程对象会sleeping(需要等cpu来调用)
启动线程
线程对象.start()
理解部分:
1.等待区(休眠区):创建线程的时候和线程执行期被停止
2.执行区
3.死亡区 等待销毁
5.查看获取的线程列表
threading.current_thread() 获取当前执行代码的线程
threading.enumerate() 获取当前程序活动线程的列表
每一个程序至少有一个线程---->主线程
总结:1.线程没有执行是不会进入到活动的线程列表
<只有线程启动,线程才会加入到活动列表>
2.子线程进行的时候,主线程也在进行
3.如果主线程直接退出没有等待子线程,子线程会被销毁
例:
from time import sleep
# from threading import Thread
import threading
def cc(cctv):
for i in range(cctv):
print("ss",i)
sleep(0.5)
def bb(cctv):
for i in range(cctv):
print("bb",i)
sleep(1)
if __name__ == '__main__':
print("创建主线程",threading.current_thread())
print("执行主线程",threading.enumerate())
print("创建线程22222",threading.current_thread())
cc1 = threading.Thread(target=cc,name="11",kwargs={"cctv":3})
bb1 = threading.Thread(target=bb,name="22",args=(3,))
cc1.start()
print("执行线程22222",threading.enumerate())
bb1.start()
print("创建线程33333",threading.current_thread())
print("执行线程33333",threading,enumerate)
6.线程注意点
a).线程之间执行是无序的
b).主线程会等待所有的子线程结束后才结束
C).守护主线程
daemon = True :守护主线程
设置守护主线程,让创建的线程停止daemon = True
7.设置守护主线程
daemon = True
就是守护了后面的线程就不执行了
例:
from threading import Thread
def cc():
for i in range(3):
print("我在打游戏",i)
if __name__ == '__main__':
cc1 = Thread(target=cc)
cc1.daemon = True
cc1.start()
print("你不可以在我前面打游戏")
8.阻塞主线程(使用阻塞的前提,子线程被守护)
a).卡时间点 用sleep()
b).使用 线程名字.join([time]) time默认为子线程执行完毕的时间none
例:
from threading import Thread
def cc():
for i in range(3):
print("我在打游戏",i)
if __name__ == '__main__':
cc1 = Thread(target=cc)
cc1.daemon = True
cc1.start()
cc1.join()
print("你不可以在我前面打游戏")
就是用join()了可以阻塞主线程让线程执行完毕
9.自定义线程
必须重写Thread类中的run方法
run方式是用来执行任务的(绑定任务的)
执行自定义的线程时,不能通过线程对象调用run()
而是使用start()方法执行线程
例:
import threading
class MY(threading.Thread):
def __init__(self,show1,show2):
super(MY,self).__init__()
self.show1 = show1
self.show2 = show2
def test1(self):
print(self.show1)
def test2(self):
print(self.show2)
def run(self) :
self.test1()
self.test2()
s = MY("测试1","测试2")
s.start()
就是在run()中调用在下面执行
自定义线程不能指定target,因为自定义线程里面的任务都统一在run方法里面执行
10.多线程---共享全局变量
主线程和多个子线程会使用定义的全局变量
解决资源竞争问题
让线程‘同步’:协同步调,让多个线程执行有一个顺序
a.让先启动的线程阻塞主线程 join
b.使用互斥锁
缺点:一旦让线程同步了,会导致程序的效率变低,使本来的多线程变为单线程
例:
import threading
from time import sleep
sss = []
def A():
for i in range(5):
sss.append(i)
sleep(0.1)
print("存入的值",sss)
def B():
print("读取的值",sss)
if __name__ == '__main__':
aa = threading.Thread(target=A)
bb = threading.Thread(target=B)
aa.start()
aa.join()
bb.start()
11.互斥锁
互斥锁:保证只有一个线程拿到全局资源
使用过程:
a.需要给使用同一个全局资源的线程上锁
b.先上锁占用全局资源
c.执行完之后再去释放锁
代码使用过程:
a.先创建锁 threading.Lock()--->全局锁的对象
该互斥锁对象必须为全局变量
b.给线程上锁 互斥锁的对象.acquire()
c.释放锁 互斥锁的对象.release()
互斥锁为资源引入一个状态:锁定/非锁定
例:
import threading
num = 0
s = threading.Lock() #创建锁
def A():
s.acquire() #上锁
for i in range(1000000):
global num
num+=1
print("A:",num)
s.release() #释放锁
def B():
s.acquire() #上 锁
for i in range(1000000):
global num
num+=1
print("B:",num)
s.release() #释放锁
if __name__ == '__main__':
aa = threading.Thread(target=A)
bb = threading.Thread(target=B)
aa.start()
# aa.join() #用.join()一样
bb.start()
12 死锁
死锁:一直等待对方释放锁,一但发生就会造成应用的停止响应,避免死锁就要在合适的地方释放锁