一 传统编程的缺陷
传统编程的弊端:
# 必须按照顺序执行,多个任务无法同时在还行
import time
def sing():
for i in range(5):
print("sing: hero")
time.sleep(1) # 每唱一次,等1秒再唱
def dance():
for i in range(5):
print("dance: swan")
time.sleep(1) # 每唱一次,等1秒再跳
def main():
sing()
dance()
if __name__ == "__main__":
main()
2个任务花费的时间是10秒,如果要边跳边唱,其实2个任务是可以在最长的那个任务完成时全部完成的。
实现多任务编程的方式有很多,如:多进程、多线程、协程等。
二 使用多进程方式实现多任务
# 必须按照顺序执行,多个任务无法同时在还行
import time
import multiprocessing
def sing():
for i in range(5):
print("sing: hero")
time.sleep(1) # 每唱一次,等1秒再唱
def dance():
for i in range(5):
print("dance: swan")
time.sleep(1) # 每唱一次,等1秒再跳
def main():
p1 = multiprocessing.Process(target=sing)
p2 = multiprocessing.Process(target=dance)
p1.start()
p2.start()
if __name__ == "__main__":
main()
三 进程的一些操作
通过 htop
命令可以查看详细的进程列表。
注意:使用kill -9 pid
杀死主进程后,子进程不会被杀死,此时命令行也会无法正常退出,因为该命令的信号是发给了主进程来执行杀死任务,子进程由于没有父进程,变成了孤儿进程,之后被init进程领养。也就是说杀死主进程后,子进程的父进程称为了init进程。
通过 os.getpid()
可以获取到当前进程的pid,os.getppid()
可以获取父进程id。
四 进程间通信
进程之间无法直接进行通信,因为他们是互相独立的应用程序。
进程之间要实现通信,常见的方式有:socket等,python中可以使用队列方式实现:
queue = multiprocessing.Queue(3)
queue.put("111")
queue.put(222)
# 取数据
res = queue.get()
print(res)
# 判断: q.full() q.empty()
五 进程池
p = multiprocessing.Pool(3)
六 多线程方式实现多任务
import time
import threading
def sing():
for i in range(5):
print("sing: hero")
time.sleep(1) # 每唱一次,等1秒再唱
def dance():
for i in range(5):
print("dance: swan")
time.sleep(1) # 每唱一次,等1秒再跳
def main():
t1 = threading.Thread(target=sing) # 创建一个线程对象
t2 = threading.Thread(target=dance) # 创建一个线程对象
t1.start() # 开启线程
t2.start() # 开启线程
if __name__ == "__main__":
main()
七 线程相关API
通过 threading.Thread()
可以创建线程,threading.enumerate()
也可以查看当前所有的线程:
import time
import threading
def sing():
for i in range(5):
print("sing: hero")
time.sleep(1) # 每唱一次,等1秒再唱
def dance():
for i in range(5):
print("dance: swan")
time.sleep(1) # 每唱一次,等1秒再跳
def main():
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start()
t2.start()
while True:
length = len(threading.enumerate())
print("当前线程数为:%d" % length)
if length <= 1:
break
time.sleep(0.5)
if __name__ == "__main__":
main()
八 同步与死锁
线程是共享全局变量的,这样就会造成数据的混乱:
import time
import threading
# 定义共享的全局变量
num = 0
def add100():
global num
for i in range(100000):
num = num + 0.00001
def add1000():
global num
for i in range(100000):
num = num + 1000
def main():
t1 = threading.Thread(target=add100)
t2 = threading.Thread(target=add1000)
t1.start()
t2.start()
time.sleep(5)
print(num) # 每次输出的结果是不相同的
if __name__ == "__main__":
main()
使用互斥锁实现同步的方案:
import time
import threading
# 定义共享的全局变量
num = 0
def add100():
global num
mutex.acquire() # 加锁:若已经加锁,则会直到锁被揭开
for i in range(100000):
num = num + 0.00001
mutex.release() # 解锁
def add1000():
global num
mutex.acquire() # 加锁:若已经加锁,则会直到锁被揭开
for i in range(100000):
num = num + 1000
mutex.release() # 解锁
# 创建互斥锁,默认不会上锁
mutex = threading.Lock()
def main():
t1 = threading.Thread(target=add100)
t2 = threading.Thread(target=add1000)
t1.start()
t2.start()
time.sleep(5)
print(num) # 100000001.0 永远不会变
if __name__ == "__main__":
main()