网络并发编程面试题61-70
什么是线程安全,什么是互斥锁?
'''
每个对象都对应于一个可称为" 互斥锁"的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
同一个进程中的多线程之间是共享系统资源的,多个线程同时对一个对象进行操作,一个线程操作尚未结束,
另一个线程已经对其进行操作,导致最终结果出现错误,此时需要对被操作对象添加互斥锁,保证每个线程对该对象的操作都得到正确的结果。
'''
说说下面几个概念:同步,异步,阻塞,非阻塞?
'''
同步:多个任务之间有先后顺序执行,一个执行完下个才能执行。
异步:多个任务之间没有先后顺序,可以同时执行有时候一个任务可能要在必要的时候获取另一个同时执行的任务的结果,这个就叫回调!
阻塞:如果卡住了调用者,调用者不能继续往下执行,就是说调用者阻塞了。
非阻塞:如果不会卡住,可以继续执行,就是说非阻塞的。
同步异步相对于多任务而言,阻塞非阻塞相对于代码执行而言。
'''
什么是僵尸进程和孤儿进程?怎么避免僵尸进程?
'''
孤儿进程:父进程退出,子进程还在运行的这些子进程都是孤儿进程,孤儿进程将被 init 进程(进程号为 1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:进程使用fork创建子进程,如果子进程退出,而父进程并没有调用 wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中的这些进程是僵尸进程。
避免僵尸进程的方法:
-fork两次用孙子进程去完成子进程的任务;
-用wait()函数使父进程阻塞;
-使用信号量,在 signal handler 中调用waitpid,这样父进程不用阻塞。
'''
Python中的进程与线程的使用场景?
# 多进程适合在 CPU 密集型操作(cpu 操作指令比较多,如位数多的浮点运算)。
# 多线程适合在 IO 密集型操作(读写数据操作较多的,比如爬虫)。
线程是并发还是并行,进程是并发还是并行?
# 线程是并发,进程是并行;
# 进程之间相互独立,是系统分配资源的最小单位,同一个线程中的所有线程共享资源。
并行(parallel)和并发(concurrency)?
'''
并行:同一时刻多个任务同时在运行。
并发:在同一时间间隔内多个任务都在运行,但是并不会在同一时刻同时运行,存在交替执行的情况。
实现并行的库有:multiprocessing
实现并发的库有:threading
程序需要执行较多的读写、请求和回复任务的需要大量的 IO 操作,IO密集型操作使用并发更好。
CPU运算量大的程序程序,使用并行会更好。
IO密集型和CPU密集型区别?
IO密集型:系统运作,大部分的状况是CPU在等 I/O (硬盘/内存)的读/写。
CPU密集型:大部份时间用来做计算、逻辑判断等 CPU动作的程序称之CPU密集型。
'''
使用udp发送/接收数据步骤:
1.创建客户端套接字
2.发送/接收数据
3.关闭套接字
import socket
def main():
# 1、创建udp套接字
# socket.AF_INET 表示IPv4协议 AF_INET6 表示IPv6协议
# socket.SOCK_DGRAM 数据报套接字,只要用于udp协议
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2、准备接收方的地址
# 元组类型 ip是字符串类型 端口号是整型
dest_addr = ('192.168.113.111', 8888)
# 要发送的数据
send_data = "我是要发送的数据"
# 3、发送数据
udp_socket.sendto(send_data.encode("utf-8"), dest_addr)
# 4、等待接收方发送的数据 如果没有收到数据则会阻塞等待,直到收到数据
# 接收到的数据是一个元组 (接收到的数据, 发送方的ip和端口)
# 1024 表示本次接收的最大字节数
recv_data, addr = udp_socket.recvfrom(1024)
# 5、关闭套接字
udp_socket.close()
if __name__ == '__main__': 22. main() 编码的转换
str -->bytes: encode编码
bytes--> str: decode()解码
UDP绑定端口号:
1.创建socket套接字
2.绑定端口号
3.接收/发送数据
4.关闭套接字
import socket
def main():
# 1、创建udp套接字
# socket.AF_INET 表示IPv4协议 AF_INET6 表示IPv6协议
# socket.SOCK_DGRAM 数据报套接字,只要用于udp协议
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2、绑定端口
# 元组类型 ip一般不写 表示本机的任何的一个ip
local_addr = ('', 7777)
udp_socket.bind(local_addr)
# 3、准备接收方的地址
# 元组类型 ip是字符串类型 端口号是整型
dest_addr = ('192.168.113.111', 8888)
# 要发送的数据
send_data = "我是要发送的数据"
# 4、发送数据
udp_socket.sendto(send_data.encode("utf-8"), dest_addr)
# 5、等待接收方发送的数据 如果没有收到数据则会阻塞等待,直到收到数据
# 接收到的数据是一个元组 (接收到的数据, 发送方的ip和端口)
# 1024 表示本次接收的最大字节数
recv_data, addr = udp_socket.recvfrom(1024)
# 6、关闭套接字
udp_socket.close()
if __name__ == '__main__':
main() 注意点:绑定端口要在发送数据之前进行绑定。
TCP客户端的创建流程:
1.创建TCP的socket套接字
2.连接服务器
3.发送数据给服务器端
4.接收服务器端发送来的消息
5.关闭套接字
import socket
def main():
# 1、创建客户端的socket
# socket.AF_INET 表示IPv4协议 AF_INET6 表示IPv6协议
# socket.SOCK_STREAM 流式套接字,只要用于TCP 协议
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2、构建目标地址
server_ip = input("请输入服务器端的IP地址:")
server_port = int(input("请输入服务器端的端口号:"))
# 3、连接服务器
# 参数:元组类型 ip 是字符串类型 端口号是整型
client_socket.connect((server_ip, server_port))
# 要发送给服务器端的数据
send_data = "我是要发送给服务器端的数据"
# 4、发送数据
client_socket.send(send_data.encode("gbk"))
# 5、接收服务器端恢复的消息, 没有消息会阻塞
# 1024表示接收的最大字节数
recv_date= client_socket.recv(1024)
print("接收到的数据是:", recv_date.decode('gbk'))
# 6、关闭套接字
client_socket.close()
if __name__ == '__main__':
main()
TCP服务器端的创建流程
1.创建TCP服务端的socket
2.bing绑定ip地址和端口号
3.listen使套接字变为被动套接字
4.accept取出一个客户端连接,用于服务
5.recv/send接收和发送消息
6.关闭套接字
import socket
def main():
# 1、创建tcp服务端的socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2、绑定
server_socket.bind(('', 8888))
# 3、listen使套接字变为被动套接字
server_socket.listen(128)
# 4、如果有新的客户端来链接服务器,那么就产生一个新的套接字专门为这个客户端服务
# client_socket用来为这个客户端服务
# tcp_server_socket就可以省下来专门等待其他新客户端的链接
client_socket, client_addr = server_socket.accept()
# 5、接收客户端发来的消息
recv_data = client_socket.recv(1024)
print("接收到客户端%s的数据:%s" % (str(client_addr), recv_data.decode('gbk')))
# 6、回复数据给客户端
client_socket.send("收到消息".encode('gbk'))
# 7、关闭套接字
client_socket.close()
server_socket.close()
if __name__ == '__main__':
main()