python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用
代码:
import threading
import time
def hello():
for index in range(5):
print("我是 hello-------------------",end="\n")
time.sleep(1)
def word():
for index in range(5):
print("我是 word" + str(index))
time.sleep(2)
if __name__ == "__main__":
# 创建线程对象
t1 = threading.Thread(target=hello)
# 创建线程对象
t2 = threading.Thread(target=word)
# 执行线程
t1.start()
# 执行线程
t2.start()
注意
可以明显看出使用了多线程并发的操作,花费时间要短很多
当调用start()时,才会真正的创建线程,并且开始执行
target=方法名称,这个方法名称不能加括号,如果加上括号就变成了执行方法了而不是创建线程对象
for threa_name in threading.enumerate():
print(threa_name)
一般主线程会等待所有的子线程结束后才结束,并且每个线程默认有一个名字,python会自动为线程指定一个名字。当线程的run()方法结束时该线程完成。无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。
继承Thread类实现多线程
python的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。而创建自己的线程实例后,通过Thread类的start方法,可以启动该线程,交给python虚拟机进行调度,当该线程获得执行的机会时,就会调用run方法执行线程。
# 继承Thread类实现多线程
import threading
class MyThread(threading.Thread):
# 继承thread类之后一般都需要重写一下run方法
def run(self):
print("开始执行run方法")
if __name__ == "__main__":
# 创建线程对象
t = MyThread()
# 执行线程时,会自动调用run方法
t.start()
实例
import threading
import time
num = 100
def set_num():
global num
for i in range(5):
num += 1
print("set_num-----------" + str(num))
def get_num():
print("get_num-----------" + str(num))
if __name__ == "__main__":
t1 = threading.Thread(target=set_num)
t2 = threading.Thread(target=get_num)
t1.start()
t2.start()
time.sleep(5)
print("__main__-----------" + str(num))
注意:使用args参数传值的话,必须使用可变类型的数据(列表、字典、可变集合),否则将不能共享
import threading
import time
def set_num(temp_num):
temp_num.append(5)
print("set_num-----------" + str(temp_num))
def get_num(temp_num):
# 停顿1秒来保证数据共享效果
time.sleep(1)
print("get_num-----------" + str(temp_num))
if __name__ == "__main__":
# 使用args参数传值的话,必须使用可变类型的数据(列表、字典、可变集合),否则将不能共享
nums = [1, 2, 3, 4]
t1 = threading.Thread(target=set_num, args=(nums,))
t2 = threading.Thread(target=get_num, args=(nums,))
t1.start()
t2.start()
time.sleep(5)
print("__main__-----------" + str(nums))
共享全局变量会出现抢资源问题,好比1个线程同时执行到为一个变量赋值时还没有保存成功,第二个线程开始执行将值改变之后,在到第1个线程执行保存值时就将刚刚第二个线程的值直接覆盖问题。
实例
import threading
import time
num = 0
def set_num1():
global num
for i in range(100000):
num += 1
print("set_num-----------" + str(num))
def set_num2():
global num
for i in range(100000):
num += 1
# 互斥所关闭
print("get_num-----------" + str(num))
if __name__ == "__main__":
t1 = threading.Thread(target=set_num1)
t2 = threading.Thread(target=set_num2)
t1.start()
t2.start()
time.sleep(5)
print("__main__-----------" + str(num))
应该结果是200000才对,现在最后的答案完全不对,所以就会出现多个线程同时对同一个全局变量操作,会出现资源竞争问题,从而数据结果会不正确,那么这个时候就需要使用同步来解决此问题,互斥锁就是让某一个线程操作变量的时候要么操作完毕,要么不操作
互斥锁
threading模块中定义了Lock类,可以方便的处理锁定:
# 创建锁
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 释放
mutex.release()
注意:
如果这个锁之前是没有上锁的,那么acquire不会堵塞
如果在调用acquire对这个锁上锁之前 它已经被 其他线程上了锁,那么此时acquire会堵塞,直到这个锁被解锁为止
这个时候就可以解决刚刚的问题了,最终结果永远是正确的
封装dup多线程聊天机器人
import socket
import threading
class ClientSocket(threading.Thread):
def __init__(self):
# 没有该语句的话则会出现Python RuntimeError: thread.__init__() not called异常
threading.Thread.__init__(self)
self.udp_clien = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.udp_clien.bind(("", 8888))
def send_msg(self):
while True:
msg = input("请输入数据")
self.udp_clien.sendto(msg.encode("utf-8"), ("192.168.153.1", 8080))
def rev_msg(self):
while True:
msg = self.udp_clien.recvfrom(1024)
print(f"{msg[0].decode('utf-8')},{msg[1]}")
def run(self):
t1 = threading.Thread(target=self.send_msg)
t2 = threading.Thread(target=self.rev_msg)
t1.start()
t2.start()
if __name__ == "__main__":
c = ClientSocket()
c.start()
完