定义
- 进程是系统进行资源分配和调度的一个独立单位.
- 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
区别
- 一个程序至少有一个进程,一个进程至少有一个线程.
- 线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。
- 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
- 线程不能够独立执行,必须依存在进程中
- 线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。
1、 多线程案例
udp多人聊天
# -*- coding:utf-8 -*-
import socket
import threading
def send_msg(udp_socket):
while True:
# 1. 从键盘输入数据
msg = input("\n请输入要发送的数据:\n")
# 2. 输入对方的ip地址
dest_ip = input("\n请输入对方的ip地址:\n")
# 3. 输入对方的port
dest_port = int(input("\n请输入对方的port:\n"))
# 4. 发送数据
udp_socket.sendto(msg.encode("utf-8"), (dest_ip, dest_port))
def recv_msg(udp_socket):
'''接收数据'''
while True:
#接收数据
recv_msg = udp_socket.recvfrom(1024)
#解码
recv_ip = recv_msg[1]
recv_msg = recv_msg[0].decode('utf8')
print('%s:%s'%(str(recv_ip),recv_msg))
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#绑定
udp_socket.bind(('',7788))
#创建子线程接收数据
t = threading.Thread(target=recv_msg,args=(udp_socket,))
t.start()
#主线程用来检测键盘数据并且发送
send_msg(udp_socket)
if __name__ == "__main__":
main()
锁
# 创建锁
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 释放
mutex.release()
-
锁的好处:
确保了某段关键代码只能由一个线程从头到尾完整地执行
-
锁的坏处:
阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁
- 避免死锁
程序设计时要尽量避免(银行家算法)
添加超时时间等
2、多进程
- multiprocessing.Pool常用函数解析:
- apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
- close():关闭Pool,使其不再接受新的任务;
- terminate():不管任务是否完成,立即终止;
- join():主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用;
文件拷贝
# -*- coding:utf-8 -*-
import multiprocessing
import os,time,random
def copy_file(queue,file_name,source_folder_name,dest_folder_name):
f_read = open(source_folder_name + '/' + file_name,'rb')
f_write = open(dest_folder_name + '/' + file_name,'wb')
while True:
content = f_read.read(1024)
if content:
f_write.write(content)
else:
break
f_read.close()
f_write.close()
# 发送已经拷贝完毕的文件名字
queue.put(file_name)
def main():
# 获取要复制的文件夹
source_folder_name = r'H:\学习\test'
# 整理目标文件夹
dest_folder_name = source_folder_name + '副本'
#创建目标文件夹
try:
os.mkdir(dest_folder_name)
except:
pass
#获取这个文件夹的所有文件名
file_names = os.listdir(source_folder_name)
#创建Queue
queue = multiprocessing.Manager().Queue()
#创建进程池
pool = multiprocessing.Pool(3)
for file_name in file_names:
pool.apply_async(copy_file,args=(queue,file_name,source_folder_name,dest_folder_name))
pool.close() #不再接收新的任务
#主进程显示进度
all_filenum = len(file_names)
while True:
file_name = queue.get()
if file_name in file_names:
file_names.remove(file_name)
copy_rate = (all_filenum - len(file_names)) * 100/all_filenum
print("\r%.2f...(%s)" % (copy_rate, file_name) + " " * 50, end="")
if copy_rate >= 100:
break
print()
if __name__ == '__main__':
main()
3、协程
属于并发
# -*- coding:utf-8 -*-
from gevent import monkey
import gevent
import urllib.request
# 有耗时操作时需要
monkey.patch_all()
def my_downLoad(url):
print('GET: %s' % url)
resp = urllib.request.urlopen(url)
data = resp.read()
print('%d bytes received from %s.' % (len(data), url))
gevent.joinall([
gevent.spawn(my_downLoad, 'http://www.baidu.com/'),
gevent.spawn(my_downLoad, 'https://www.csdn.net/'),
gevent.spawn(my_downLoad, 'https://y.qq.com/'),
])