python基础03

线程

我们通常依靠线程来解决需要同时执行多个代码流的要求,python通过一个内置模块提供了多线程的支持——threading模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import threading

# 线程回调函数
def worker():
for i in range(1000):
print(i, threading.current_thread().name)

if __name__ == "__main__":
# 创建一个线程对象,指定起始位置和名称
t = threading.Thread(target=worker, name="worker")
# 执行线程
t.start()

# 输出主线程的名称
for i in range(1000):
print(i, threading.current_thread().name)

# 主线程等到其他线程执行完毕
t.join()

线程函数还可以在构建线程的时候传递参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import threading
# 一个变参函数,接收的数据被打包成元组
def worker_thread1(*args):
print(type(args), args)

# 一个变参函数,接收的数据被打包成字典
def worker_thread2(**kwargs):
print(type(kwargs), kwargs)

# 可以传入元组和键值对
def worker_thread3(*args, **kwargs):
print(type(args), args)
print(type(kwargs), kwargs)

if __name__ == "__main__":
threading.Thread(target=worker_thread1, args=(1, 2, 3)).start()
# 传递字典到线程回调函数中时,键应该是一个字符串
threading.Thread(target=worker_thread2, kwargs={"a": 1, "b": 2}).start()
threading.Thread(target=worker_thread3, args=(1, 2, 3), kwargs={"a": 1, "b": 2}).start()

image-20191204134737136

多个线程同时访问一个变量会存在危险

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import threading
import time
# 使用线程模块中提供的锁 Lock
lock = threading.Lock()

# 提供一个线程的起始位置(线程回调函数)
def worker():
for i in range(1000):
# 将需要保护的代码添加到 acquire 和 release 中间
time.sleep(0.001)
lock.acquire()
print(i, threading.current_thread().name)
lock.release()

# 如果当前的模块是主模块就执行
if __name__ == "__main__":
# 创建了一个线程对象,并直接执行
threading.Thread(target=worker, name="worker").start()

# 输出主线程的名称,应该和线程的输出是交替的
for i in range(1000):
time.sleep(0.001)
lock.acquire()
print(i, threading.current_thread().name)
lock.release()

套接字

仍然用socket函数创建套接字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# server.py
# 导入套接字模块 socket
import socket
# 创建一个用于网络连接的套接字,使用 TCP
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定套接字到执行的IP和端口,必须是元祖
server.bind(("127.0.0.1", 8080))
# 设置套接字为监听状态,同一时刻能够接收的数量
server.listen(socket.SOMAXCONN)
# 等待客户端的连接
client, address = server.accept()
# 与客户端进行交互
while True:
s = input(">> ")
# 发送的数据需要编码
client.send(s.encode())
# 等待客户端的回复,需要解码
print(client.recv(100).decode())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# client.py
# 导入套接字模块
import socket

# 创建套接字,使用 TCP 协议
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 等待客户端的连接
client.connect(("127.0.0.1", 8080))
# 和服务器交互
while True:
print(client.recv(100).decode())
s = input(">> ")
# 发送的数据必须是byte类型的,需要进行编码
client.send(s.encode())

单线程通信模型中,发送和接收会被阻塞,需要与多线程结合使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# server.py
# 导入套接字模块 socket
import socket
import threading

def reciver_thread(client):
while True:
print(client.recv(100).decode())

# 创建一个用于网络连接的套接字,使用 TCP
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定套接字到执行的IP和端口,必须是元祖
server.bind(("127.0.0.1", 8080))
# 设置套接字为监听状态,同一时刻能够接收的数量
server.listen(socket.SOMAXCONN)
# 等待客户端的连接
client, address = server.accept()
# 创建接收者线程
threading.Thread(target=reciver_thread, args=(client,)).start()
# 与客户端进行交互
while True:
s = input(">> ")
# 发送的数据需要编码
client.send(s.encode())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# client.py
# 导入套接字模块
import socket
import threading

def reciver_thread(client):
while True:
print(client.recv(100).decode())

# 创建套接字,使用 TCP 协议
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 等待客户端的连接
client.connect(("127.0.0.1", 8080))
# 创建接收者线程
threading.Thread(target=reciver_thread, args=(client,)).start()
# 和服务器交互
while True:
s = input(">> ")
# 发送的数据必须是byte类型的,需要进行编码
client.send(s.encode())

多个客户机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# server.py
# 导入套接字模块 socket
import socket
import threading

# 维护一个列表,保存所有在线的客户端
clients = []

def reciver_thread(client):
while True:
try:
# 接收某一个客户端的消息,需要转发
msg = client.recv(100)
for i in clients:
if i != client:
i.send(msg)
except Exception as e:
for i in clients:
if i == client:
clients.remove(i)
print(client.getpeername(), "下线了")
break

# 创建一个用于网络连接的套接字,使用 TCP
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定套接字到执行的IP和端口,必须是元祖
server.bind(("127.0.0.1", 8080))
# 设置套接字为监听状态,同一时刻能够接收的数量
server.listen(socket.SOMAXCONN)
# 与客户端进行交互
while True:
# 连接到的客户端,输出它的地址
client, address = server.accept()
# 将连接到的客户端添加到在线列表中
clients.append(client)
print(client, "上线了")
# 每一个客户端都应该有一个独立的接受数据的线程
threading.Thread(target=reciver_thread, args=(client,)).start()

编码方式

1
2
3
4
5
6
7
# Python 向 C++ 发送一个 char*(GBK) 类型的字符串
client.send("你好".encode("gbk"))
print(client.recv(100).decode("gbk"))

# Python 向 C++ 发送一个 wchar_t*(UTF16LE) 类型的字符串
client.send("你好".encode("utf-16LE"))
print(client.recv(100).decode("utf-16LE"))