-
多任务的概念
就是操作系统可以同时运行多个任务。打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务- 并发:指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
- 并行:指的是任务数小于等于cpu核数,即任务真的是一起执行的
一般而言,并发的场景更多一点
-
线程
在python中,我们调用thread模块进行多线程任务,python的threading模块是对thread做了一些包装的,可以更加方便的被使用
使用多线程的例子
import threading
import time
def saySorry():
print("亲爱的,我错了,我能吃饭了吗?")
time.sleep(1)
if __name__ == "__main__":
for i in range(5):
t = threading.Thread(target=saySorry) # 这里声明线程
t.start() #启动线程,即让线程开始执行
我们使用threading.enumerate()来查看线程数量,它会返回一个列表
令一种使用线程的方式(类)
import threading
import time
class MyThread(threading.Thread):
def run(self):
for i in range(3):
time.sleep(1)
msg = "I'm "+self.name+' @ '+str(i) #name属性中保存的是当前线程的名字
print(msg)
if __name__ == '__main__':
t = MyThread()
t.start()
这里在这个类中必须要有run方法,在我们启动线程后,python会自动调用run方法启动线程。这是写进thread里面去的,没有run方法则不会被执行
- 优缺点
在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据
缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)(出现资源竞争问题)
这里我们采用互斥锁来保证安全
import threading
import time
g_num = 0
def test1(num):
global g_num
for i in range(num):
mutex.acquire() # 上锁
g_num += 1
mutex.release() # 解锁
print("---test1---g_num=%d"%g_num)
def test2(num):
global g_num
for i in range(num):
mutex.acquire() # 上锁
g_num += 1
mutex.release() # 解锁
print("---test2---g_num=%d"%g_num)
# 创建一个互斥锁
# 默认是未上锁的状态
mutex = threading.Lock()
# 创建2个线程,让他们各自对g_num加1000000次
p1 = threading.Thread(target=test1, args=(1000000,))
p1.start()
p2 = threading.Thread(target=test2, args=(1000000,))
p2.start()
# 等待计算完成
while len(threading.enumerate()) != 1:
time.sleep(1)
print("2个线程对同一个全局变量操作之后的最终结果是:%s" % g_num)
互斥锁保证代码可以完整执行完,但是,加互斥锁我们也要注意出现死锁的现象
- 多线程UDP版聊天器案例
import socket
import threading
def send_msg(udp_socket,recv_ip,recv_port):
"""发送消息"""
# 输入要发送的消息
while True:
send_data = input("请输入需要发送的数据:")
udp_socket.sendto(send_data.encode("utf-8"),(recv_ip,recv_port))
def recv_msg(udp_socket):
"""接收消息"""
while True:
data = udp_socket.recvfrom(1024)
print(data)
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 绑定
udp_addr = ("",9632)
udp_socket.bind(udp_addr)
# 接收方的地址
recv_ip = input("请输入接收方的地址:")
recv_port = int(input("请输入接收方的端口:"))
t_send = threading.Thread(target=send_msg, args=(udp_socket,recv_ip,recv_port))
t_recv = threading.Thread(target=recv_msg, args=(udp_socket,))
if __name__ == "__main__":
main()
使用了多线程就能够让收发消息同时进行,不用像以前没有多线程版本的一样需要等待。多线程极大的提高了程序的效率