线程理论
什么是线程?
进程其实是一个资源单位,真正被cpu执行的其实时进程里面的线程
# 进程只是提供给线程需要的各项资源
进程类似于工程 线程类似于工厂里面的一条条流水线
所有的进程里最少有一个线程
进程之间的数据默认是隔离的,但是同一个进程内的多个线程的数据是共享的。
开设线程的两种方式
对比开设进程来看一下开设线程的方式
开设进程:
1、重新申请一块内存空间
2、将所需的资源全部导入
开设线程:
进程的两部操作都不需要进行,所以开设线程的消耗的资源远比开设的进程所占用的资源少!!
代码实现开设线程
from threading import Thread # 导入开设线程模块
import time
def test(name):
print(f'{name} running')
time.sleep(3)
print(f'{name} over')
t = Thread(target=test, args=('gary',))
t.start()
print('主进程')
# 使用类的方法实现开设线程 同样的效果
class MyClass(Thread):
def __init__(self,name)
super().__init__()
self.name = name
def run(self):
print('%s is running' % self.name)
time.sleep(3)
print('%s is over' % self.name)
obj = MyClass('jason')
obj.start()
print('主线程')
可以看到线程的执行速度是非常快的
线程对象的其他方法
1、join方法
2、获取进程号(可验证同一个进程内可以开设多少个线程,同一个进程中的父线程号是相同的)
格式:查看线程号:getpid()
查看父线程号:getppid()
守护线程
主线程的结束意味着正好进程的结束,所以主线程需要等待里面所有的非首付线程结束才能结束
代码实现守护线程
from threading import Thread
import time
def foo():
print(123)
time.sleep(3)
print('end123')
def bar():
print(456)
time.sleep(1)
print('end456')
if __name__ == '__main__':
t1 = Thread(target=foo)
t2 = Thread(target=bar)
t1.daemon=True
t1.start()
t2.start()
print('main____')
线程数据共享
在同一个进程内对于线程来说所有的数据都是共享的
from threading import Thread
money = 100
def test():
global money # 局部修改全局 100 变成 999
money = 999
t = Thread(target=test)
t.start()
t.join()
print(money)
# 输出为 999 # 这就说明在同一个进程内所有线程的数据是共享的
from multiprocessing import Process
money = 100
def test():
global money # 局部修改全局 100 变成 999
money = 999
t = Process(target=test)
t.start()
t.join()
print(money)
# 输出结果为100 # 这里就说明再开设子进程时是重新申请了一个内存空间与主进程的数据是不共享的
线程互斥锁
#不加锁:并发执行,速度快,数据不安全
from threading import Thread, Lock
from multiprocessing import Lock
import time
num = 100
def test(mutex):
global num
mutex.acquire() # 以下内容加锁
# 先获取num的数值
tmp = num
# 模拟延迟效果
time.sleep(0.1)
# 修改数值
tmp -= 1
num = tmp
mutex.release() # 释放锁
t_list = []
mutex = Lock()
for i in range(100):
t = Thread(target=test, args=(mutex,))
t.start()
t_list.append(t)
# 确保所有的子线程全部结束
for t in t_list:
t.join()
print(num)
# 输出结果为0
这里可能有疑问:既然加锁会让运行变成串行,那么我在start之后立即使用join,就不用加锁了啊,也是串行的效果啊
没错:在start之后立刻使用jion,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是
start后立即join:任务内的所有代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的
单从保证数据安全方面,二者都可以实现,但很明显是加锁的效率更高.
练习:TCP服务端实现并发
# 服务端:
import socket
from threading import Thread
from multiprocessing import Process
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5) # 限制半连接池数为5
def talk(sock): # 将与用户端交互的代码块封装
while True:
try:
data = sock.recv(1024)
if len(data) == 0: break
print(data.decode('utf8'))
sock.send(data + b'go out!')
except ConnectionResetError as e:
print(e)
break
sock.close()
while True:
sock, addr = server.accept() # 与客户端建立连接 来一个客户端就开设一个线程
print(addr)
# 开设多进程或者多线程 这里开设多线程
t = Thread(target=talk, args=(sock,))
t.start()