【6.0】多线程操作
【一】threading模块介绍
- multiprocess模块的完全模仿了threading模块的接口
- 二者在使用层面,有很大的相似性,因而不再详细介绍
官网链接:https://docs.python.org/3/library/threading.html?highlight=threading
【二】开启线程的两种方式
- 开启线程不需要在main下面执行代码,直接书写即可
- 但是我们还是习惯性的将启动命令写在main下面
【1】方式一:直接调用 Thread 方法
from multiprocessing import Process
from threading import Thread
import time
def task(name):
print(f'当前任务:>>>{name} 正在运行')
time.sleep(3)
print(f'当前任务:>>>{name} 结束运行')
def Thread_main():
t = Thread(target=task, args=("dream",))
# 创建线程的开销非常小,几乎代码运行的一瞬间线程就已经创建了
t.start()
'''
当前任务:>>>dream 正在运行this is main process!
this is main process!
当前任务:>>>dream 结束运行
'''
def Process_main():
p = Process(target=task, args=("dream",))
p.start()
'''
this is main process!
当前任务:>>>dream 正在运行
当前任务:>>>dream 结束运行
'''
if __name__ == '__main__':
Thread_main()
# Process_main()
print('this is main process!')
【2】方式二:继承 Thread 父类
from threading import Thread
import time
class MyThread(Thread):
def __init__(self, name):
# 重写了别人的方法,又不知道别人的方法里面有什么, 就调用父类的方法
super().__init__()
self.name = name
# 定义 run 函数
def run(self):
print(f'{self.name} is running')
time.sleep(3)
print(f'{self.name} is ending')
def main():
t = MyThread('dream')
t.start()
print(f'this is a main process')
"""
dream is running
this is a main process
dream is ending
"""
if __name__ == '__main__':
main()
【三】一个进程下开启多个线程和多个子进程的区别
【1】谁的开启速度快
from threading import Thread
from multiprocessing import Process
import time
def work():
print('hello')
def timer(func):
def inner(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
print(f'函数 {func.__name__} 运行时间为:{time.time() - start_time}')
return res
return inner
@timer
def work_process():
# 在主进程下开启子进程
t = Process(target=work)
t.start()
print('主线程/主进程')
'''
主线程/主进程
函数 work_process 运行时间为:0.0043752193450927734
hello
'''
@timer
def work_thread():
# 在主进程下开启线程
t = Thread(target=work)
t.start()
print('主线程/主进程')
'''
打印结果:
hello
主线程/主进程
函数 work_thread 运行时间为:0.0001499652862548828
'''
if __name__ == '__main__':
# part1 : 多线程
work_thread()
# part2 : 多进程
work_process()
【2】查看pid
from threading import Thread
from multiprocessing import Process
import os
def work():
print('hello', os.getpid())
def work_thread():
# part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
t1 = Thread(target=work)
t2 = Thread(target=work)
t1.start()
t2.start()
print('主线程/主进程pid', os.getpid())
# hello 5022
# hello 5022
# 主线程/主进程pid 5022
def work_process():
# part2:开多个进程,每个进程都有不同的pid
p1 = Process(target=work)
p2 = Process(target=work)
p1.start()
p2.start()
print('主线程/主进程pid', os.getpid())
# 主线程/主进程pid 5032
# hello 5034
# hello 5035
if __name__ == '__main__':
# part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
work_thread()
# part2:开多个进程,每个进程都有不同的pid
work_process()
【3】同一进程内的线程共享该进程的数据?
from threading import Thread
from multiprocessing import Process
def work():
global n
n = 0
def work_process():
n = 100
p = Process(target=work)
p.start()
p.join()
print('主', n) # 毫无疑问子进程p已经将自己的全局的n改成了0,但改的仅仅是它自己的,查看父进程的n仍然为100
# 主 100
def work_thread():
n = 1
t = Thread(target=work)
t.start()
t.join()
print('主', n) # 查看结果为1,因为同一进程内的线程之间共享进程内的数据
if __name__ == '__main__':
# part1 多进程 : 子进程只改自己的
work_process()
# part2 多线程: 数据发生错乱,同一进程内的线程之间共享数据
work_thread()
【四】多线程并发的socket服务端
【1】基础版(单服务端)
(1)客户端
from socket import *
# 不写参数:默认是TCP协议
# (1)创建客户端对象
client = socket()
# (2)绑定 IP PORT
# (2)建立链接桥梁 --(呼应客户端的 ip 和 port)
IP = '127.0.0.1'
PORT = 8082
client.connect((IP, PORT))
# (4)链接循环
while True:
# (4.1)向服务端发数据
msg_to_server = b'this is a message'
client.send(msg_to_server)
# 接受服务器返回的数据
data_from_server = client.recv(1024)
# (4.3)接收到客户端的信息
msg_from_client = data_from_server.decode('utf-8')
print(msg_from_client)
(2)服务端
from threading import Thread
from multiprocessing import Process
from socket import *
'''
服务端的三大特点:
(1)要有固定的IP和PORT
(2)24h不间断提供服务
(3)能够支持并发
'''
# 不写参数:默认是TCP协议
# (1)创建服务器对象
server = socket()
# (2)建立链接桥梁 --(呼应客户端的 ip 和 port)
IP = '127.0.0.1'
PORT = 8082
server.bind((IP, PORT))
# (3)半连接池创建
server.listen(5)
# 正常版本
def normal_version():
# (4)链接循环
while True:
# (4.1) 接受连接对象和 ip port
conn, addr = server.accept()
while True:
# 捕获异常并抛出
try:
msg_from_client = conn.recv(1024)
# (4.2)接受的信息为空时,会无限循环
if len(msg_from_client) == 0:
break
# (4.3)接收到客户端的信息
msg_from_client = msg_from_client.decode('utf-8')
print(msg_from_client)
# (4.4)返回给客户端信息
msg_to_client = msg_from_client.upper()
msg_to_client = msg_to_client.encode('utf-8')
conn.send(msg_to_client)
except Exception as e:
print(e)
break
# (4.5)关闭链接
conn.close()
# 将接受处理数据部分封装成函数调用
def talk(conn):
while True:
# 捕获异常并抛出
try:
msg_from_client = conn.recv(1024)
# (4.2)接受的信息为空时,会无限循环
if len(msg_from_client) == 0:
break
# (4.3)接收到客户端的信息
msg_from_client = msg_from_client.decode('utf-8')
print(msg_from_client)
# (4.4)返回给客户端信息
msg_to_client = msg_from_client.upper()
conn.send(msg_to_client)
except Exception as e:
print(e)
break
# (4.5)关闭链接
conn.close()
if __name__ == '__main__':
normal_version()
【2】升级版(多服务端)
(1)服务端
from threading import Thread
from multiprocessing import Process
from socket import *
'''
服务端的三大特点:
(1)要有固定的IP和PORT
(2)24h不间断提供服务
(3)能够支持并发
'''
# 不写参数:默认是TCP协议
# (1)创建服务器对象
server = socket()
# (2)建立链接桥梁 --(呼应客户端的 ip 和 port)
IP = '127.0.0.1'
PORT = 8083
server.bind((IP, PORT))
# (3)半连接池创建
server.listen(5)
# 正常版本
def normal_version():
# (4)链接循环
while True:
# (4.1) 接受连接对象和 ip port
conn, addr = server.accept()
while True:
# 捕获异常并抛出
try:
msg_from_client = conn.recv(1024)
# (4.2)接受的信息为空时,会无限循环
if len(msg_from_client) == 0:
break
# (4.3)接收到客户端的信息
msg_from_client = msg_from_client.decode('utf-8')
print(msg_from_client)
# (4.4)返回给客户端信息
msg_to_client = msg_from_client.upper()
msg_to_client = msg_to_client.encode('utf-8')
conn.send(msg_to_client)
except Exception as e:
print(e)
break
# (4.5)关闭链接
conn.close()
# 将接受处理数据部分封装成函数调用
def talk(conn):
while True:
# 捕获异常并抛出
try:
msg_from_client = conn.recv(1024)
# (4.2)接受的信息为空时,会无限循环
if len(msg_from_client) == 0:
break
# (4.3)接收到客户端的信息
msg_from_client = msg_from_client.decode('utf-8')
print(msg_from_client)
# (4.4)返回给客户端信息
msg_to_client = msg_from_client.upper()
msg_to_client = msg_to_client.encode('utf-8')
conn.send(msg_to_client)
except Exception as e:
print(e)
break
# (4.5)关闭链接
conn.close()
# 多线程版本
def threading_version(conn):
t = Thread(target=talk, args=(conn,))
t.start()
# 多进程版本
def process_version(conn):
p = Process(target=talk, args=(conn,))
p.start()
def main_t():
# (4)链接循环
while True:
# (4.1) 接受连接对象和 ip port
conn, addr = server.accept()
threading_version(conn)
def main_p():
# (4)链接循环
while True:
# (4.1) 接受连接对象和 ip port
conn, addr = server.accept()
process_version(conn)
if __name__ == '__main__':
main_t()
(2)客户端
from socket import *
# 不写参数:默认是TCP协议
# (1)创建客户端对象
client = socket()
# (2)绑定 IP PORT
# (2)建立链接桥梁 --(呼应客户端的 ip 和 port)
IP = '127.0.0.1'
PORT = 8083
client.connect((IP, PORT))
# (4)链接循环
while True:
# (4.1)向服务端发数据
msg_to_server = b'this is a message'
client.send(msg_to_server)
# 接受服务器返回的数据
data_from_server = client.recv(1024)
# (4.3)接收到客户端的信息
msg_from_client = data_from_server.decode('utf-8')
print(msg_from_client)
【五】线程对象的 join 方法
from threading import Thread
import time
def task(name):
print(f'the task {name} is beginning')
time.sleep(3)
print(f'the task {name} is ending')
def main():
t = Thread(target=task, args=('dream',))
t.start()
# 主线程等待子进程结束之后再运行
t.join()
print(f'the task is main task')
if __name__ == '__main__':
main()
# the task dream is beginning
# the task dream is ending
# the task is main task
【六】同一个进程下的多个线程之间数据是共享的
from threading import Thread
import time
money = 999
def task():
global money
money = 99
print(f'task中的money:>>>>{money}')
def main():
print(f'子进程之前的money:>>>>{money}')
t = Thread(target=task)
t.start()
print(f'子进程之后的money:>>>>{money}')
if __name__ == '__main__':
main()
# 子进程之前的money:>>>>999
# task中的money:>>>>99
# 子进程之后的money:>>>>99
【七】线程对象属性及其他方法
【1】同一个进程下的进程号相同
from threading import Thread
import time, os
def task():
print(f'this is a task PID {os.getpid()}')
def main():
t = Thread(target=task)
t.start()
print(f'this is a main PID {os.getpid()}')
if __name__ == '__main__':
main()
# this is a task PID 6496
# this is a main PID 6496
【2】获取当前进程的名字current_thread
from threading import Thread, active_count, current_thread
import time, os
def task():
# 获取当前线程的名字
print(f'this is a task name {current_thread().name}')
def main():
t = Thread(target=task)
t.start()
print(f'this is a main name {current_thread().name}')
if __name__ == '__main__':
main()
# this is a task name Thread-1
# this is a main name MainThread
【3】统计当前活跃的线程数active_count
- 这里统计的线程数 2 个
- 可能已经有一个子线程已经死了
from threading import Thread, active_count, current_thread
import time, os
def task():
# 获取当前线程的名字
print(f'this is a task name {current_thread().name}')
def main():
t = Thread(target=task)
t1 = Thread(target=task)
t.start()
t1.start()
# 统计当前活跃的线程数
print(f'this is a main process active_process {active_count()}')
print(f'this is a main name :>>>> {current_thread().name}')
if __name__ == '__main__':
main()
# this is a task name Thread-1
# this is a task name Thread-2
# this is a main process active_process 1
# this is a main name :>>>> MainThread
- 这里统计的活跃进程数是 3 个
- 让我们第一个子进程晚一点死
from threading import Thread, active_count, current_thread
import time, os
def task():
# 获取当前线程的名字
print(f'this is a task name {current_thread().name}')
time.sleep(1)
def main():
t = Thread(target=task)
t1 = Thread(target=task)
t.start()
t1.start()
# 统计当前活跃的线程数
print(f'this is a main process active_process {active_count()}')
print(f'this is a main name :>>>> {current_thread().name}')
if __name__ == '__main__':
main()
# this is a task name Thread-1
# this is a task name Thread-2
# this is a main process active_process 3
# this is a main name :>>>> MainThread
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/17982384