Day9 PythonWeb全栈课程课堂内容
1. 线程方法的补充
import threading
import os
from threading import active_count, current_thread
def task():
print("hello")
print(os.getpid())
if __name__ == "__main__":
t = threading.Thread(target=task)
t.start()
print(os.getpid())
- 主线程和子线程在同一个进程中。
import threading
import os
from threading import active_count, current_thread
def task():
print("hello")
if __name__ == "__main__":
t = threading.Thread(target=task)
t.start()
print(active_count())
- 返回活跃的线程。返回数字1,
import threading
import os
from threading import active_count, current_thread
import time
def task():
print("hello")
time.sleep(1)
if __name__ == "__main__":
t = threading.Thread(target=task)
t.start()
print(active_count())
- 返回活跃的线程。返回数字2。
- 加了time模块之后,子线程时间变长,活跃数增加。原因子线程已经结束,检测代码只检测到主线程。
import threading
import os
from threading import active_count, current_thread
import time
def task():
print("hello")
print(current_thread().name) # Thread-1
time.sleep(1)
if __name__ == "__main__":
t = threading.Thread(target=task)
t.start()
print(current_thread().name) # MainThread
- 获取线程的名字。
2. 守护线程的补充
import threading
import time
def foo():
print('123')
time.sleep(1)
print('end123')
def foo1():
print('456')
time.sleep(1)
print('end456')
if __name__ == "__main__":
t1 = threading.Thread(target=foo)
t2 = threading.Thread(target=foo1)
t1.daemoin = True
t1.start()
t2.start()
print('MainThread')
- 守护线程有影响,原因是t2子线程并没有结束,而所以守护线程并没有结束,最终
end123
依旧输出。
3. 多任务版的UDP聊天
import socket
import threading
import time # 防止输入输出同时出现
def send_data(udp_socket):
while True:
time.sleep(1)
send_data = input('Please your input data:')
udp_socket.sendto(send_data.encode('utf-8'), ('192.168.1.107', 7070))
def recv_data(udp_socket):
while True:
data = udp_socket.recvfrom(1024)
print('%s: %s' % (str(data[1]), data[0].decode('utf-8')))
def main():
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_socket.bind(('192.168.1.107', 7070))
t_send = threading.Thread(target=send_data, args=(udp_socket,))
t_recv = threading.Thread(target=recv_data, args=(udp_socket,))
t_send.start()
t_recv.start()
if __name__ == '__main__':
main()
4. 多任务TCP
# tcp 客户端
import threading
import socket
client = socket.socket()
client.connect(('127.0.0.1', 7070))
while True:
client.send(b'hello world')
data = client.recv(1024)
print(data.decode('utf-8'))
# tcp 服务端
import threading
import socket
tcp_server = socket.socket()
tcp_server.bind(('127.0.0.1', 7070))
def main():
while True:
tcp_server.listen(128)
new_client_socket, addr = tcp_server.accept()
while True:
recv_data = new_client_socket.recv(1024)
print(recv_data.decode('utf-8'))
if recv_data:
new_client_socket.send('123'.encode('utf-8'))
else:
break
new_client_socket.close()
tcp_server.close()
if __name__ == '__main__':
for i in range(3):
t = threading.Thread(target=main)
t.start()
import threading
import time
num = 100
def func():
global num
num += 1
print('func--%d'%num)
def func1():
print('func1--%d'%num)
def main():
t1 = threading.Thread(target=func)
t2 = threading.Thread(target=func1)
t1.start()
t2.start()
print('main--%d'%num)
if __name__ == '__main__':
main()
- 证明2
import threading
import time
num = 100
def func(nums):
global num
num += nums
print('func--%d'%num)
def func1(nums):
global num
num += nums
print('func1--%d'%num)
def main():
t1 = threading.Thread(target=func,args=(100,))
t2 = threading.Thread(target=func1,args=(200,))
t1.start()
t2.start()
print('main--%d'%num)
if __name__ == '__main__':
main()
- 说明了线程是共享全局变量的。
6. 共享全局变量资源竞争
import threading
import time
num = 100
def func(nums):
global num
for i in range(nums):
num += 1
print('func--%d'%num)
def func1(nums):
global num
for i in range(nums):
num += 1
print('func1--%d'%num)
def main():
t1 = threading.Thread(target=func,args=(100,))
t2 = threading.Thread(target=func1,args=(200,))
t1.start()
t2.start()
print('main--%d'%num)
if __name__ == '__main__':
main()
- 当循环次数增大的时候,出现了资源的竞争。
原理:
- CPU在运行线程时,循环次数越大越有可能发生此概率。
验证此原理:
-
dis
模块,先被编译为Python字节码, 再由Python虚拟机来执行字节码。import dis def adds(a): a += 1 print(dis.dis(adds))
显示内容 | 说明 |
---|---|
LOAD_FAST 0(a) | 进行a参数的操作 |
LOAD_CONST 1(1) | |
INPLACE_ADD | 进行加操作 |
STORE_FAST 0(a) | 赋值给a |
LOAD_CONST 0(None) | |
RETURN_VALUE | 返回值 |
7. 解决共享全局变量资源竞争的问题
前提
- 使用
互斥锁
来解决此问题:
何为互斥锁
- 当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制
- 某个线程要更改共享数据时,先将其锁定,此时资源的状态为"锁定",其他线程不能改变,只到该线程释放资源,将资源的状态变成"非锁定",其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
import threading
# 创建锁,返回一个值。
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 解锁
mutex.release()
- 使用在线程当中。
import threading
import time
num = 0
mutex = threading.Lock()
def func(nums):
global num
mutex.acquire()
for i in range(nums):
num += 1
mutex.release()
print('func--%d'%num)
def func1(nums):
global num
mutex.acquire()
for i in range(nums):
num += 1
mutex.release()
print('func1--%d'%num)
def main():
t1 = threading.Thread(target=func,args=(10000000,))
t2 = threading.Thread(target=func1,args=(10000000,))
t1.start()
t2.start()
time.sleep(4)
print('main--%d'%num)
if __name__ == '__main__':
main()
- 当代码中嵌套两把锁,如果继续使用
threading.Lock()
就会将程序停滞。如果需要嵌套的两把锁就必须得使用threading.RLock()
。应用场景,爬取数据时,防止大量数据产生错误。
import threading
import time
num = 0
mutex = threading.RLock() # 可重入的锁
def func(nums):
global num
mutex.acquire()
for i in range(nums):
num += 1
mutex.release()
print('func--%d'%num)
def func1(nums):
global num
mutex.acquire()
mutex.acquire()
for i in range(nums):
num += 1
mutex.release()
mutex.release()
print('func1--%d'%num)
def main():
t1 = threading.Thread(target=func,args=(10000000,))
t2 = threading.Thread(target=func1,args=(10000000,))
t1.start()
t2.start()
time.sleep(4)
print('main--%d'%num)
if __name__ == '__main__':
main()
- 加锁一定的数量,就必须有相同数量的解锁。
8. 死锁
- 在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
import threading
import time
class MyThread1(threading.Thread):
def run(self):
# 对mutexA上锁
mutexA.acquire()
# mutexA上锁后,延时1秒,等待另外那个线程 把mutexB上锁
print(self.name+'----do1---up----')
time.sleep(1)
# 此时会堵塞,因为这个mutexB已经被另外的线程抢先上锁了
mutexB.acquire()
print(self.name+'----do1---down----')
mutexB.release()
# 对mutexA解锁
mutexA.release()
class MyThread2(threading.Thread):
def run(self):
# 对mutexB上锁
mutexB.acquire()
# mutexB上锁后,延时1秒,等待另外那个线程 把mutexA上锁
print(self.name+'----do2---up----')
time.sleep(1)
# 此时会堵塞,因为这个mutexA已经被另外的线程抢先上锁了
mutexA.acquire()
print(self.name+'----do2---down----')
mutexA.release()
# 对mutexB解锁
mutexB.release()
mutexA = threading.Lock()
mutexB = threading.Lock()
if __name__ == '__main__':
t1 = MyThread1()
t2 = MyThread2()
t1.start()
t2.start()
避免死锁
- 程序设计时要尽量避免
- 添加超时时间等
9. 线程同步
import threading
class XiaoAi(threading.Thread):
def __init__(self, name='小爱'):
super(XiaoAi, self).__init__(name=name)
def run(self):
print("{}:在".format(self.name))
print("{}:你猜猜现在几点了".format(self.name))
class TianMao(threading.Thread):
def __init__(self, name='天猫'):
super(TianMao, self).__init__(name=name)
def run(self):
print("{}:小爱同学".format(self.name))
print("{}:现在几点了?".format(self.name))
if __name__ == "__main__":
xiaoai = XiaoAi()
tianmao = TianMao()
xiaoai.start()
tianmao.start()
- 而修改
xiaoai.start()
和tianmao.start()
顺序之后依旧如此。
import threading
class XiaoAi(threading.Thread):
def __init__(self, name='小爱'):
super(XiaoAi, self).__init__(name=name)
def run(self):
print("{}:在".format(self.name))
print("{}:你猜猜现在几点了".format(self.name))
class TianMao(threading.Thread):
def __init__(self, name='天猫'):
super(TianMao, self).__init__(name=name)
def run(self):
print("{}:小爱同学".format(self.name))
print("{}:现在几点了?".format(self.name))
if __name__ == "__main__":
xiaoai = XiaoAi()
tianmao = TianMao()
tianmao.start()
xiaoai.start()
- 加入锁之后和加入延时之后,你会发现可行。但这只是表面,当代码特别多时,不建议采取此方法。
import threading
import time
class XiaoAi(threading.Thread):
def __init__(self, lock, name='小爱'):
super(XiaoAi, self).__init__(name=name)
self.lock = lock
def run(self):
self.lock.acquire()
print("{}:在".format(self.name))
self.lock.release()
time.sleep(1)
self.lock.acquire()
print("{}:你猜猜现在几点了".format(self.name))
self.lock.release()
class TianMao(threading.Thread):
def __init__(self, lock, name='天猫'):
super(TianMao, self).__init__(name=name)
self.lock = lock
def run(self):
self.lock.acquire()
print("{}:小爱同学".format(self.name))
self.lock.release()
time.sleep(1)
self.lock.acquire()
print("{}:现在几点了?".format(self.name))
self.lock.release()
if __name__ == "__main__":
mutex = threading.Lock()
xiaoai = XiaoAi(mutex)
tianmao = TianMao(mutex)
tianmao.start()
xiaoai.start()
- 现在使用
Condition
之后。
# @Time : 2021/1/11 23:18
# @Author : Sam
# @File : 线程同步3.py
# @Software: PyCharm
import threading
import time
class XiaoAi(threading.Thread):
def __init__(self, cond, name='小爱'):
super(XiaoAi, self).__init__(name=name)
self.cond = cond
def run(self):
self.cond.wait()
print("{}:在".format(self.name))
self.cond.notify()
self.cond.wait()
print("{}:你猜猜现在几点了".format(self.name))
class TianMao(threading.Thread):
def __init__(self, cond, name='天猫'):
super(TianMao, self).__init__(name=name)
self.cond = cond
def run(self):
print("{}:小爱同学".format(self.name))
self.cond.notify()
self.cond.wait()
print("{}:现在几点了?".format(self.name))
self.cond.notify()
if __name__ == "__main__":
cond = threading.Condition()
xiaoai = XiaoAi(cond)
tianmao = TianMao(cond)
tianmao.start()
xiaoai.start()
-
为什么会产生错误呢?
-
我们现在从
Condition
源码中可以得知:我们需要加锁。
-
所以我们在程序中加上锁。
# @Time : 2021/1/11 23:18
# @Author : Sam
# @File : 线程同步3.py
# @Software: PyCharm
import threading
import time
class XiaoAi(threading.Thread):
def __init__(self, cond, name='小爱'):
super(XiaoAi, self).__init__(name=name)
self.cond = cond
def run(self):
with self.cond:
self.cond.wait()
print("{}:在".format(self.name))
self.cond.notify()
self.cond.wait()
print("{}:你猜猜现在几点了".format(self.name))
self.cond.notify()
class TianMao(threading.Thread):
def __init__(self, cond, name='天猫'):
super(TianMao, self).__init__(name=name)
self.cond = cond
def run(self):
with self.cond:
print("{}:小爱同学".format(self.name))
self.cond.notify()
self.cond.wait()
print("{}:现在几点了?".format(self.name))
self.cond.notify()
if __name__ == "__main__":
cond = threading.Condition()
xiaoai = XiaoAi(cond)
tianmao = TianMao(cond)
xiaoai.start()
tianmao.start()