Python——多线程多进程与网络套接字编程
2018-05-15 本文已影响0人
Jason_c8d4
一、多进程和多线程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
在Python中是能够使用多线程,来调度多核CPU来同时工作。
import time
from multiprocessing import Process
# process 进程 启用多进程在控制台中一共输出Boom XiaKaLaKa,10次
count = 0
def output(string):
global count
inner_count = 0
while count < 10:
print(string, end='', flush=True)
count += 1
inner_count += 1
time.sleep(0.5)
print('\n%s打印了%d次\n' % (string, inner_count))
def main():
Process(target=output, args=('Boom',)).start() # 开启多进程调用start()方法
Process(target=output, args=('XiaKaLaKa',)).start()
if __name__ == '__main__':
main()
在Python中也能使用多线程来实现异步任务处理, 但是只能调用一个CPU来全力执行线程中的任务
from threading import Thread
from time import sleep
def output():
while True:
print('Boom', end='', flush=True)
sleep(0.001)
def main():
Thread(target=output).start() # 启用多线程调用output函数
while True:
print('XiaKaLaKa', end='', flush=True)
sleep(0.001)
if __name__ == '__main__':
main()
当然了,这样写,参数太长了,一般情况下,都是将线程写成一个类,这样,能对线程进行更加丰富的封装和定制
from threading import Thread
from time import sleep
from random import randint
# 创建线程的两种方式
# 1\. 直接创建Thread对象并通过target参数指定线程启动后要执行的任务
# 2\. 继承Thread自定义线程 通过重写run方法指定线程启动后执行的任务
class PrintThread(Thread): # 创建类, 继承线程
def __init__(self, string, count): # 初始化参数
super().__init__()
self._string = string # 需要打印的字符串
self._count = count # 计数
def run(self):
for _ in range(self._count):
print(self._string, end='', flush=True)
sleep(randint(0,2)) # 随机休眠,让每个线程都有占用CPU资源的机会
def main():
PrintThread('Boom', 100).start() # 开启自定义的线程
PrintThread('XiaKaLaKa', 100).start()
if __name__ == '__main__':
main()
下面我们来模拟一个下载文件的行为
import time
import random
from threading import Thread
from multiprocessing import process
# 如果多个任务之间没有任何的关联(独立子任务)而且希望利用cpu的多核特性
# 那么我们推荐使用多进程
class DownloadTask(Thread):
def __init__(self, filename): # 初始化线程构造方法
super().__init__()
self.filename = filename
def run(self):
print('开始下载%s...' % self.filename)
delay = random.randint(5, 15) # 随机数模拟下载时间
time.sleep(delay) # 线程休眠
print('%s下载完成,用时%d秒.' % (self.filename, delay))
def main():
start = time.time() # 保存开始时间
t1 = DownloadTask('Python从入门到住院.pdf')
t2 = DownloadTask('Android从入门到放弃.pdf')
t1.start() # 开启线程
t2.start()
t1.join() # 等待子线程执行完成
t2.join()
end = time.time() # 保存结束时间
print('总共耗费了%f秒' % (end - start)) # 计算耗费的时间
if __name__ == '__main__':
main()
资源的保护,当我们同时开启多个线程同时去访问一个资源的时候,那个资源就是一个临界资源,临界资源,是无法正确的被多线程给读写的,这时候就资源添加一个锁,当有线程在使用的时候就将资源锁起来,使用完后又释放出去,这样就能保证数据的有效读写。
import time
from threading import Thread, Lock
class Account(object): # 账户类
def __init__(self): # 初始化账户
self._balance = 0
self._lock = Lock()
@property
def balance(self): # 获取余额
return self._balance
def deposit(self, money): # 存钱
# 当多个线程同时访问一个资源的时候,就有可能因为竞争,导致资源的状态错误
# 被多个线程访问的资源,我们通常称之为临界资源,对临界资源的访问需要加上保护
if money > 0:
self._lock.acquire() # 开启资源的锁保护
try:
new_balance = self._balance + money
time.sleep(0.01)
self._balance = new_balance
finally:
self._lock.release() # 释放资源的锁保护
class AddMoneyThread(Thread): # 存钱线程
def __init__(self, account): # 初始化传入账户对象
super().__init__()
self._account = account
def run(self):
self._account.deposit(1) # 线程执行时存入一块钱
def main():
account = Account() # 新建账户对象
tlist = [] # 线程列表
for _ in range(100): # 循环100次,进行存钱操作
t = AddMoneyThread(account) # 新建一个线程,传入账户对象
tlist.append(t) # 添加 到线程列表中
t.start() # 开起线程
for t in tlist:
t.join() # 等待线程执行结束
print('账户余额%d元' % account.balance) # 打印出当前账户的余额
if __name__ == '__main__':
main()
二、套接字(Socket)编程
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
# 服务端,等待客户端的接入
from socket import socket, AF_INET, SOCK_STREAM # 需要导入socket包
from time import sleep, localtime, time
from datetime import datetime
def main():
# 创建基于TCP协议的套接字对象
# 因为我们做的是应用级产品或服务所以可以利用现有的传输服务来实现数据传输
server = socket(AF_INET, SOCK_STREAM) # 这两个是默认参数,默认的TCP连接,也可以直接socket()来创建对象
# 绑定IP地址(网络上主机的身份标识)和端口(用来区分不同服务的IP地址扩展)
server.bind(('10.7.189.55', 6310))
# 开始监听客户端的连接,队列数量为512
server.listen(512)
print('服务器已经启动正在监听...')
client, addr = server.accept() # 开始等待接入
while True:
# 通过accept方法接收客户端的连接
# accept方法是一个阻塞式的方法 如果没有客户端连接上老
# 那么accept方法就好让代码阻塞 直到有客户端连接成功才返回对象
# accept方法返回的一个原则,元组中的第一个值是代表客户端的对象
# 元组中的第二值又是一个元组 其中有客户端的IP地址和客户端的地址
# client, addr = server.accept()
print(addr, '连接')
date_time = datetime
print(client.recv(512).decode('utf-8'))
message = input("需要的发的消息")
client.send(str(message).encode('utf-8'))
# client.close()
if __name__ == '__main__':
main()
# 客户端的连接
from socket import socket
def main():
client = socket() # 创建socket对象
client.connect(('10.7.189.55', 6310)) # 连接服务器地址
data = client.send('你1234e44好'.encode('utf-8')) # 发送消息
print(type(data))
print(data)
client.close() # 关闭连接
if __name__ == '__main__':
main()
服务器的输出如下:
服务器已经启动正在监听…
(‘10.7.189.55’, 50984) 连接
你1234e44好
这样就完成了一个简单的Socket连接