线程的概念
线程(英语:thread):是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
多任务
就是电脑同时执行多个任务,但是其实是轮转,并不是真的一起运行,叫时间片轮转
并发: CPU小于执行的任务
并行: CPU大于执行的任务
线程
主线程:
代码从上往下执行,主线程会默认等到子线程执行完毕然后在关闭
import threading
import time
def sing():
for i in range(5):
print('正在唱歌。。。')
time.sleep(1)
def dance():
for i in range(5):
print('正在跳舞。。。')
time.sleep(1)
def main():
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start() # 这里创建子线程并且运行子线程
t2.start()
if __name__ == '__main__':
main()
注:子线程里面还可以创建子线程
守护主线程:就是不等待子线程执行完成直接走
setDaemon(True)
等到子线程执行完, 默认是主线程先走,最后会等待子线程执行结束才会停止
jion()
查看线程的数量
print(threading.enumerate())
线程执行没有顺序
线程真正创建并运行是在start那里
通过类来创建线程
import threading
class A(threading.Thread):
def run(self): # 只能通过run方法添加子线程
for i in range(5):
print(i)
if __name__ == '__main__':
a = A()
a.start()
线程间的通信
修改全局变量一定是要加上global吗?
int是不可变对象,所以不管怎么样都需要加上global
如 :
num = 100
def demo():
global num
num += 100
print(num)
print(num) # 100
demo() # 200
print(num) # 200
但是列表就不一样,列表是可变的,对于可变的对象,只要没有改变其指向就不需要添加global
如:
lis = [11, 22]
def demo():
lis.append(33) # 其指向并没有改变,所以可以进行添加
lis = lis + [44] # 赋值的指向已经改变了,不能进行添加
demo()
多线程共享全局变量
import threading
import time
num = 100
def demo1():
global num
num += 1
print('demo1-->%d' % num)
def demo2():
print('demo2-->%d' % num)
if __name__ == '__main__':
t1 = threading.Thread(target=demo1)
t2 = threading.Thread(target=demo2)
t1.start()
time.sleep(1)
t2.start()
time.sleep(1)
print('main-->%d' % num)
"""
demo1-->101
demo2-->101
main-->101
"""
传参
args=(), 必须传一个元组
全局变量的资源竞争
因为线程的执行顺序是没有的,所以谁先抢到资源谁就先执行
如:
import threading
num = 0
def demo1(nums):
for i in range(nums):
global num
num += 1 # 1.获取num的值 2.获取n+=1 3.保存num的值 之所以值不一样是因为两个函数里面的这3步不一定都是执行完
print("demo1---%d" % num)
def demo2(nums):
for i in range(nums):
global num
num += 1
print("demo2---%d" % num)
def main():
t1 = threading.Thread(target=demo1, args=(100000,))
t2 = threading.Thread(target=demo2, args=(100000,))
t1.start()
t2.start()
print("main()---%d" % num)
if __name__ == '__main__':
main()
“”“
demo1---141635
demo2---184111
main()---184111
”“”
得到的结果不一定正确,这是因为上面分解的步骤不一定是按照想的那样执行,所以值是不正确的,但是我们可以通过加上锁来完成。
如何在线程上添加锁呢?
import threading
import time
mutex = threading.Lock()
num = 0
def demo1(nums):
# 添加互斥锁
mutex.acquire()
for i in range(nums):
global num
num += 1 # 1.获取num的值 2.获取n+=1 3.保存num的值 之所以值不一样是因为两个函数里面的这3步不一定都是执行完
mutex.release()
print("demo1---%d" % num)
def demo2(nums):
mutex.acquire()
for i in range(nums):
global num
num += 1
mutex.release()
print("demo2---%d" % num)
def main():
t1 = threading.Thread(target=demo1, args=(100000,))
t2 = threading.Thread(target=demo2, args=(100000,))
t1.start()
t2.start()
time.sleep(1)
print("main()---%d" % num)
if __name__ == '__main__':
main()
"""
demo1---100000
demo2---200000
main()---200000
"""
还可以用可重入锁RLock()可以同时加上多个锁,但是必须解那么多个锁
线程同步
用线程完成
天猫精灵:小爱同学
小爱同学:在
天猫精灵:现在几点了?
小爱同学:你猜猜现在几点了
Condition
- 可添加等待,然后再唤醒从而完成
- wait() 是等待,必须要有 notify() 把他唤醒
import threading
class XiaoAi(threading.Thread):
def __init__(self, cond):
super().__init__(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))
class TianMao(threading.Thread):
def __init__(self, cond):
super().__init__(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()
def main():
cond = threading.Condition()
t1 = TianMao(cond)
t2 = XiaoAi(cond)
t2.start()
t1.start()
udp聊天器
利用解耦的思想,发送和接受数据单独写一个函数,然后在利用线程来完成多任务
import threading
import socket
def send_data(udp_socket):
while True:
data = input('需要发送的数据:')
udp_socket.sendto(data.encode('gbk'), ('192.168.0.119', 8080))
def recv_data(udp_socket):
udp_socket.bind(('', 7892))
while True:
recv_data = udp_socket.recvfrom(1024) # 返回的是一个元组,(内容,对方ip端口)
print(recv_data[0].decode('gbk'))
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 创建子线程同时进行
t_recv = threading.Thread(target=recv_data, args=(udp_socket,))
t_send = threading.Thread(target=send_data, args=(udp_socket,))
t_recv.start()
t_send.start()
if __name__ == '__main__':
main()