多任务——线程
并行:一个cpu一个进程,真正的多任务
并发:一个cpu执行多个进程,假的多任务
import threading
import time
# 定义一个全局变量
g_global = 100
def text1():
global g_global
g_global += 1
print("------in text1 g_global%d-------" % g_global)
def text2():
global g_global
g_global += 1
print("------in text2 g_global%d------" % g_global)
def main():
# target=函数名(类名,类名中必须有run函数,线程会执行run函数的内容)指定将来这个线程去哪执行代码
# args=元组,指定将来调用函数(类)的时候,传递什么参数进去//例如:如果只有一个参数,(参数,)
t1 = threading.Thread(target=text1)
t2 = threading.Thread(target=text2)
# 前面的t1,t2只是创建的对象,当真正执行start时,才真正创建了子进程
t1.start()
time.sleep(1)
global g_global
t2.start()
time.sleep(1)
# 只有当子进程都结束了,主进程才会结束
print("------in main g_global%d-------" % g_global)
if __name__ == '__main__':
main()
一,global的使用
在一个函数中,对全局变量进行修改的时候,到底是否需要使用global进行说明,要看是否对全局变量的指向进行了修改,如果修改了指向,即让全局变量指向了一个新的地方,那么必须使用global,如果仅仅是修改了指向空间中的数据,此时不必使用global。数据类型为可变类型(数组,字典,集合),即可以修改指向空间中的数据,数据类型不可变(整型,字符串,元组。。。。),即不可以修改指向空间中的数据,需要用到global。一般有使用“=”时为后者。
二,互斥锁
1.进程之间的数据不能共享,但是共享同一套文件系统,所以访问同一个文件,或者同一个打印终端,是没有问题的,而共享带来的是竞争,竞争带来是错乱
2.如何控制,就是加锁处理,而互斥锁就是互相排斥,假设把多个进程比喻成多个人,互斥锁的工作原理是多个人都要去争抢同一个资源:比如抢一个房间,谁先抢到这个资源就暂时先占为己有然后上个锁,然后在他占用的时间段内别人是要等他用完
-
1. #创建锁 mutex = threading.Lock() 2. #锁定 mutex.acquire([blocking]) 3. #释放 mutex.release()
-
其中,锁定方法acquire可以有一个blocking参数。
-
如果设定blocking为True,则当前线程会堵塞,直到获取到这个锁为止(如果没有指定,那么默认为True)
如果设定blocking为False,则当前线程不会堵塞 -
创建的锁要么为全局变量类型,要么作为参数传递给子线程
from threading import Thread, Lock
import time
g_num = 0
def test1():
global g_num
for i in range(1000000):
#True表示堵塞 即如果这个锁在上锁之前已经被上锁了,那么这个线程会在这里一直等待到解锁为止
#False表示非堵塞,即不管本次调用能够成功上锁,都不会卡在这,而是继续执行下面的代码
mutexFlag = mutex.acquire(True)
if mutexFlag:
g_num += 1
mutex.release()
print("---test1---g_num=%d"%g_num)
def test2():
global g_num
for i in range(1000000):
mutexFlag = mutex.acquire(True) #True表示堵塞
if mutexFlag:
g_num += 1
mutex.release()
print("---test2---g_num=%d"%g_num)
#创建一个互斥锁
#这个所默认是未上锁的状态
mutex = Lock()
p1 = Thread(target=test1)
p1.start()
p2 = Thread(target=test2)
p2.start()
print("---g_num=%d---"%g_num)
结果:
---g_num=61866---
---test1---g_num=1861180
---test2---g_num=2000000
可以看到,加入互斥锁后,运行结果与预期相符。
三,上锁解锁过程
-
当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态。
-
每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态。
-
线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。
四,UDP实现多任务聊天器
import socket
import threading
def recv_msg(udp_socket):
while True:
recv_date = udp_socket.recvfrom(1024)
print("来自%s的消息:" % (str(recv_date[1])), end="")
print(recv_date[0].decode("utf-8"))
print("回复:", end=" ")
def send_msg(udp_socket):
while True:
send_date = input("请输入要发送的信息:")
udp_socket.sendto(send_date.encode("utf-8"), ("169.254.164.222", 8899))//地址和端口
def main():
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_socket.bind(("", 6789))
t1 = threading.Thread(target=send_msg, args=(udp_socket,))
t2 = threading.Thread(target=recv_msg, args=(udp_socket,))
t1.start()
t2.start()
if __name__ == '__main__':
main()
五,总结
锁的好处:
确保了某段关键代码只能由一个线程从头到尾完整地执行
锁的坏处:
阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,如下:
# coding=utf-8
import threading
import time
class MyThread1(threading.Thread):
def run(self):
if mutexA.acquire():
print(self.name + '----do1---up----')
time.sleep(1)
# mutexA未解锁,无法开启下一把锁,堵塞在这个位置
if mutexB.acquire():
print(self.name + '----do1---down----')
mutexB.release()
mutexA.release()
class MyThread2(threading.Thread):
def run(self):
if mutexB.acquire():
print(self.name + '----do2---up----')
time.sleep(1)
# mutexB未解锁,无法开启下一把锁,堵塞在这个位置
if mutexA.acquire():
print(self.name + '----do2---down----')
mutexA.release()
mutexB.release()
def main():
t1 = MyThread1()
t2 = MyThread2()
t1.start()
t2.start()
if __name__ == '__main__':
mutexA = threading.Lock()
mutexB = threading.Lock()
main()
输出结果:
Thread-1----do1---up----
Thread-2----do2---up----
小伙伴们可以加我的微信公众号 梦码城 ,接下来的日子里我会在公众号上发布各种与编程方面的总结学习知识,也会送各位小伙伴大量的学习资源,梦码城,期待你的加入
文章不易,道友们点个赞加个关注鼓励下梦码呗,梦码会继续努力的