1 线程的概念
由于进程是资源拥有者,创建、撤消与切换存在较大的内存开销,因此需要引入轻型进程 即线程,
进程是资源分配的最小单位,线程是CPU调度的最小单位(程序真正执行的时候调用的是线程).每一个进程中至少有一个线程。
2 进程和线程之间的关系
模拟一下 :
3 使用threading 模块创建线程
import threading #导入threading模块
def sing():
for i in range(3):
print('...唱歌...')
time.sleep(1)
def dance():
for i in range(3):
print('...跳舞...')
time.sleep(1)
def main():
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
ti.start()
t2.start()
if __name__ == '__main__':
main()
print('程序结束了....')
执行顺序:
首先程序运行时,程序从上往下走,遇到main()函数然后开始执行,执行mian()函数的函数体时又创建了两个线程我们称之为子线程,程序运行时的线程我们称之为主线程
然后子线程根据target=xxx 开始执行指定的函数(等子线程结束后主线程结束,程序结束了)
(1)线程的传递参数
给函数传递参数,使用线程的关键字args = ()进行传递参数
import threading
import time
def sing(num):
for i in range(num):
print('唱歌',i)
time.sleep(1)
def dance(num):
for i in range(num):
print('跳舞',i)
time.sleep(1)
def main():
t1 = threading.Thread(target=sing,args=(3,))
t2 = threading.Thread(target=dance,args=(3,))
t1.start()
t2.start()
if __name__ == '__main__':
main()
print('程序结束了')
"""
唱歌 0
跳舞 0
程序结束了
跳舞 1
唱歌 1
唱歌 2
跳舞 2
"""
(2) join方法
join()方法功能:当前线程执行完后其他线程才会继续执行
import threading
import time
def sing():
for i in range(3):
print('唱歌',i)
time.sleep(1)
def dance():
for i in range(3):
print('跳舞',i)
time.sleep(1)
def main():
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start()
# t1.join() #效果是t1 结束后 t2 和主线程同时执行
t2.start()
t1.join() #效果是t1 t2 子线程执行 当t1 t2 执行之后主线程才结束
if __name__ == '__main__':
main()
print('程序结束了')
(3).setDaemon() 方法
setDaemon方法 : 将当前线程设置成守护线程来守护主线程:
--当主线程结束后,守护线程也就结束,不管是否执行完成
--应用场景:qq多个聊天窗口,就是守护线程
注意:需要在子线程开启的时候设置成守护线程,否则无效
import threading
import time
def sing():
for i in range(3):
print('唱歌',i)
time.sleep(1)
def dance():
for i in range(3):
print('跳舞',i)
time.sleep(1)
def main():
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.setDaemon(True)
# t2.setDaemon(True)
t1.start()
t2.start()
if __name__ == '__main__':
main()
print('程序结束了')
(4)实例方法
线程对象的一些实例方法,了解即可
- getName(): 获取线程的名称。
- setName(): 设置线程的名称。
- isAlive(): 判断当前线程存活状态。
import time
def sing():
for i in range(3):
print('唱歌',i)
time.sleep(1)
def dance():
for i in range(3):
print('跳舞',i)
time.sleep(1)
def main():
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
print(t1.is_alive())
t1.start()
t2.start()
print(t1.isAlive())
# t1.setName('zs') #给线程设置名称
# t2.setName('ls')
# print(t1.getName()) #获取线程名称
# print(t2.getName())
if __name__ == '__main__':
main()
print('程序结束了')
"""
False
唱歌 0
跳舞 0
True
程序结束了
唱歌 1
跳舞 1
唱歌 2
跳舞 2
"""
(5) threadding 模块提供的方法
threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount():
返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
import time,threading
def sing():
for i in range(3):
print('唱歌',i)
time.sleep(1)
print(threading.current_thread())
def dance():
for i in range(3):
print('跳舞',i)
time.sleep(1)
print(threading.current_thread())
def main():
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start()
t2.start()
print(threading.current_thread())
if __name__ == '__main__':
main()
print('程序结束了')
import time,threading
def sing():
for i in range(3):
print('唱歌',i)
time.sleep(1)
def dance():
for i in range(3):
print('跳舞',i)
time.sleep(1)
def main():
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start()
t2.start()
print(threading.active_count())
if __name__ == '__main__':
main()
print('程序结束了')
4 使用继承方式开启线程
(1)定义一个类继承threading.Thread 类
(2) 复写父类的方法
import threading,time
class MyThread(threading.Thread):
def __init__(self,num):
super().__init__()
self.num = num
#1
def run(self):
for i in range(self.num):
print('i-->>',i)
time.sleep(1)
if __name__ == '__main__':
my_thread = MyThread(3)
my_thread.start()
5 线程之间共享全局变量
import threading,time
g_num = 10
def test1():
global g_num
g_num += 1
print('test1--->>',g_num)
def test2():
print('test2--->>',g_num)
def main():
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
time.sleep(1)
t2.start()
if __name__ == '__main__':
main()
"""
test1--->> 11
test2--->> 11
"""
6 共享全局变量变量的问题
多线程开发的时候共享全局变量会带来资源竞争的效果。也就是数据不安全
例
效果如上:
注意:g_num += 1 在真正执行的时候会解析成很多句代码
1 先获取g_num的值
2 获取的值 + 1
3 把结果保存到g_num中
模拟CPU执行,首先test1 获取cpu ,执行了g_num += 1 的前两步,
然后test2 获取了CPU,执行了g_num += 1 的三步,现在g_num的值是1
然后test1有获取了cpu,执行他的第三步,g_num = 1 将之前的数据覆盖掉了
因此才会出现上面的效果