线程
多线程
利用多线程实现TCP客户端:
import socket
#导入模块
import threading
def tcp_send():
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
address = (('192.168.110.128',8080))
tcp_socket.connect(address)
while True:
send_data = input('发送:')
tcp_socket.send(send_data.encode('utf-8'))
tcp_socket.close()
def tcp_recv():
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
address = (('192.168.110.128', 8080))
tcp_socket.connect(address)
while True:
re_info = tcp_socket.recv(1024)
print('接收:%s'%re_info.decode('utf-8'))
tcp_socket.close()
if __name__ == '__main__':
#定义发送的线程
p1 = threading.Thread(target=tcp_send)
# p1.setDaemon(True)
#启动发送线程
p1.start()
# 定义接收的线程
p2 = threading.Thread(target=tcp_recv)
# p2.setDaemon(True)
#启动接收线程
p2.start()
这个程序实现了客户端与服务器之间的数据发送与接收,相当于一个简易的聊天器…
多线程共享全局变量:
from threading import Thread
import time
def work1(nums):
nums.append(44)
print("----in work1---",nums)
def work2(nums):
#延时, 保证t1线程中的事情做完
time.sleep(1)
print("----in work2---",nums)
g_nums = [11,22,33]
t1 = Thread(target=work1, args=(g_nums,))
t1.start()
t2 = Thread(target=work2, args=(g_nums,))
t2.start()
总结:
1.在一个进程内的所有线程共享全局变量, 能够在不适用其他方式的前提下完成多线程之间的数据共享(这点要比多进程要好)
缺点就是, 线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)
互斥锁
#提出问题:
假设两个线程t1和t2都要对num=0进行增1运算, t1和t2都各对num修改10次, num的最终的结果应该为20。但是由于是多线程访问, 有可能出现下面情况:
在num=0时, t1取得num=0。 此时系统把t1调度为”sleeping”状态, 把t2转换为”running”状态, t2也获得num=0。 然后t2对得到的值进行加1并赋给num,使得num=1。 然后系统又把t2调度为”sleeping”, 把t1转为”running”。 线程t1又把它之前得到的0加1后赋值给num。 这样, 明明t1和t2都完成了1次加1工作, 但结果仍然是num=1。
#怎么解决:
- 系统调⽤t1, 然后获取到num的值为0, 此时上一把锁, 即不允许其他现在操作num
- 对num的值进行+1
- 解锁, 此时num的值为1, 其他的线程就可以使用num了, 而且是num的值不是0而是1
- 同理其他线程在对num进⾏修改时, 都要先上锁, 处理完后再解锁, 在上锁的整个过程中不允许其他线程访问, 就保证了数据的正确性
#使用互斥锁解决该问题:
import threading
#引用互斥锁
mutex = threading.Lock()
num = 0
def work1(number):
#当work1抢到资源时上锁
mutex.acquire()
global num
for i in range(number):
num += 1
print('work1=',num)
#执行完任务之后释放
mutex.release()
def work2(number):
#work2抢到资源时上锁
mutex.acquire()
global num
for i in range(number):
num += 1
print('work2=', num)
#执行完成之后释放
mutex.release()
if __name__ == '__main__':
#两个方法各相加100万次
t1 = threading.Thread(target=work1,args=(1000000,))
t2 = threading.Thread(target=work1,args=(1000000,))
t1.start()
t2.start()
总结
锁的好处:
确保了某段关键代码只能由一个线程从头到尾完整地执行
锁的坏处:
阻止了多线程并发执行, 包含锁的某段代码实际上只能以单线程模式执行, 效率就大大地下降了
由于可以存在多个锁, 不同的线程持有不同的锁, 并试图获取对方持有的锁时, 可能会造成死锁
解决死锁的方式:
程序设计时要尽量避免(银行家算法)
添加超时时间等