线程是什么
线程简单的理解是程序执行的一条分支,也可以是程序执行的最小单元。是被系统独立调度和分派的基本单位。
线程的创建
通过threading模块创建子线程
代码如下:
import threading
import time
#定义函数
def a():
print("唱歌")
time.sleep(0.5)
if __name__ == '__main__':
for i in range(5):
#创建线程
A=threading.Thread(target=a)
#启动线程
A.start()
结果:
线程与不加线程的对比:
由上我们知道,多线程执行节约好多时间。主线程等待子线程结束了才结束。
实例方法
通过getName()获取线程的名称
通过setName()获取线程的名称
通过isAlive()判断当前线程状态
代码说明:
import threading
import time
def sing(num):
for i in range(num):
print("正在唱歌",i)
time.sleep(1)
def dance(num):
for i in range(3):
print("正在跳舞",i)
time.sleep(1)
if __name__ == '__main__':
t1=threading.Thread(target=sing,args=(3,))
t2=threading.Thread(target=dance,args=(3,))
#设置线程名称
t1.setName("张飞")
t2.setName("关羽")
#获取线程名称
t1_name=t1.getName()
t2_name=t2.getName()
print(t1_name)
print(t2_name)
#判断线程是否存活
print(t1.is_alive())
t1.start()
t2.start()
print(t2.is_alive())
结果:
threading模块方法
通过current_thread()查线程的名称
通过threading.enumerate()获取正在运行活的线程
通过 threading.active_count()获取活的线程数 等同于 len(threading.enumerate())
代码说明:
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)
print("t2-->",threading.currentThread())
if __name__ == '__main__':
t1=threading.Thread(target=sing,args=(3,))
t2=threading.Thread(target=dance,args=(3,))
t1.start()
t2.start()
#获取当前线程的变量名称
t_nome=threading.current_thread()
#获取正在运行的活线程
print(threading.enumerate())
#获取活线程的个数
print(threading.active_count())
print(len(threading.enumerate()))
结果:
继承方式开启线程
方法:1.首先我们定义 一个类继承threading.Thread类
2.复写父类run()方法
代码:
from threading import Thread
# 继承方法创建
class MyThread(Thread):
def __init__(self,num):
super().__init__()
self.num=num
def run(self):
for i in range(self.num):
print("线程{}".format(i+1))
if __name__ == '__main__':
my_thread=MyThread(3)
my_thread.start()
结果:
从上我们可以看到通过继承的方式也可以开启线程。
线程参数顺序及传递问题
线程参数传递有三种方式:
第一种方式:通过元组进行传值
import threading
import time
def sing(a,b):
for i in range(a):
print("正在唱歌",i,b)
time.sleep(1)
def dance(c,d):
for i in range(c):
print("正在跳舞",i,d)
time.sleep(1)
if __name__ == '__main__':
t1=threading.Thread(target=sing,args=(3,"风筝舞"))
t2=threading.Thread(target=dance,args=(3,"风筝舞"))
t1.start()
t2.start()
第二种方式:通过字典传值
import threading
import time
def sing(a,b):
for i in range(a):
print("正在唱歌",i,b)
time.sleep(1)
def dance(c,d):
for i in range(c):
print("正在跳舞",i,d)
time.sleep(1)
if __name__ == '__main__':
t1=threading.Thread(target=sing,kwargs={"a":3,"b":"风筝舞"})
t2=threading.Thread(target=dance,kwargs={"c":3,"d":"风筝舞"})
t1.start()
t2.start()
第三种方式:元组和字典共同传值
import threading
import time
def sing(a,b):
for i in range(a):
print("正在唱歌",i,b)
time.sleep(1)
def dance(c,d):
for i in range(c):
print("正在跳舞",i,d)
time.sleep(1)
if __name__ == '__main__':
t1=threading.Thread(target=sing,args=(3, ),kwargs={"b":"风筝舞"})
t2=threading.Thread(target=dance,args=(3, ),kwargs={"d":"风筝舞"})
t1.start()
t2.start()
结果:
不论通过上面那种方法传值都可接收到,但在第三种上要按顺序书写。
标题线程之间共享全局变量
首先我们定义一个全局变量用代码来解释:
import random
g_num=100
def work1():
global g_num
for i in range(3):
g_num+=1
time.sleep(random.random())
print("in work1,g_num=%d"%g_num)
def work2():
global g_num
for i in range(3):
g_num += 1
time.sleep(random.random())
print("in work2,g_num=%d" % g_num)
if __name__ == '__main__':
t1 = threading.Thread(target=work1)
t2=threading.Thread(target=work2)
t1.start()
t2.start()
结果:
我们在线程之间共享全局变量,通过上面我们发现在模拟共享的过程中出现了资源竞争,会影响结果,我们可以 简单的处理这种问题,通过join()可以解决。
代码:
import threading
import time
import random
g_num=100
def work1():
global g_num
for i in range(3):
g_num+=1
time.sleep(random.random())
print("in work1,g_num=%d"%g_num)
def work2():
global g_num
for i in range(3):
g_num += 1
time.sleep(random.random())
print("in work2,g_num=%d" % g_num)
if __name__ == '__main__':
t1 = threading.Thread(target=work1)
t2=threading.Thread(target=work2)
t1.start()
t1.join()
t2.start()
结果:
这么一来一个线程执行完在让另一个线程,不会出错。下来我们就专门来解决一下这个资源竞争问题。
线程资源竞争问题
为了解决多线程几乎同时修改一个共同数据的时候,需要进行同步控制,线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。
创建互斥锁有三步:
1.创建互斥锁
2.在使用资源前锁定资源
3.使用完毕资源之后解锁资源
创建如下:
import threading
import time
g_num=0
def work1():
global g_num
for i in range(1000000):
# 上锁
lock1.acquire()
g_num += 1
# 解锁
lock1.release()
print("work1--->",g_num)
def work2():
global g_num
for i in range(1000000):
#上锁
lock1.acquire()
g_num += 1
#解锁
lock1.release()
print("work2--->",g_num)
if __name__ == '__main__':
#创建互斥锁
lock1 = threading.Lock()
#创建2个线程
t1 = threading.Thread(target=work1)
t2=threading.Thread(target=work2)
#启动两个线程
t1.start()
t2.start()
while len(threading.enumerate()) != 1:
time.sleep(1)
#在t1和t2执行完后在主线程打印g_num
print("main---->",g_num)
结果:
通过互斥锁也可以按照join()的模式运行,把一个线程运行完在运行另一个线程,
代码如下:
import threading
import time
g_num=0
def work1():
global g_num
# 上锁
lock1.acquire()
for i in range(1000000):
g_num += 1
# 解锁
lock1.release()
print("work1--->",g_num)
def work2():
global g_num
# 上锁
lock1.acquire()
for i in range(1000000):
g_num += 1
# 解锁
lock1.release()
print("work2--->",g_num)
if __name__ == '__main__':
#创建互斥锁
lock1 = threading.Lock()
#创建2个线程
t1 = threading.Thread(target=work1)
t2=threading.Thread(target=work2)
#启动两个线程
t1.start()
t2.start()
while len(threading.enumerate()) != 1:
time.sleep(1)
#在t1和t2执行完后在主线程打印g_num
print("main---->",g_num)
结果 :
这个方法固然好,但是不能把互斥锁写成死锁,在线程之间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,这样就形成了死锁。
def get_value(index):
data_list=[1,3,5,7,9]
#上锁
lock1.acquire()
# 判断下标是否越界
if index >= len(data_list):
print("下标越界!",index)
# # 解锁
# lock1.release()
return
print(data_list[index])
#解锁
lock1.release()
#创建10个线程,观察资源等待
if __name__ == '__main__':
#创建锁
lock1=threading.Lock()
# 创建10个线程
for i in range(10):
t1=threading.Thread(target=get_value,args=(i, ))
t1.start()
结果:
在越界后资源的不到释放会依然等待,如果加上return返回前加上解锁就不会处于等待状态了,就不会形成死锁。
# 解锁
# lock1.release()
线程队列Queue
q=Queue.Queue(10) 创建队列
q.put() 放入值
q.get() 取出值
q.get_nowait() 取不到数据直接抛出异常不等待
q.empty() 判断队列是否为空
q.full() 判断队列是否已满
q.task_done() 一项执行完成后发信号给 q.join()不会形成阻塞