python多线程效率低_06_多线程

9c76b9a232b645a59e3c211a38defb66.png

1.线程概述

1.线程是实现多任务编程的一种方法,可以使用计算机多核资源,是计算机核心分配的最小单位,线程由代码段,数据段,和TCB(线程控制块)组成

2.线程又称为轻量级进程,在创建和删除时消耗的计算机资源小,理论上创建和销毁线程的消耗是创建和销毁进程消耗的二十分之一

3.一个进程中的所有线程共享进程的空间资源(空间,全局变量,分配的内存等),进程中每个线程有自己的特有属性,如指令集TID等

4.多线程程序的执行顺序是不确定的,主线程会等待所有的子线程结束后才结束

5.计算机开启的线程数量建议: CPU核数 * 5

2.threading模块语法概述

importthreading

t= thread.Thread() #创建线程并返回线程对象

参数:

target: 线程函数

args: 给线程函数的位置参数(类型为元组)

kwargs: 给线程函数的字典传参(类型为字典)

name: 给线程取名字(默认为Thread-1)

t.start(): 启动线程

t.join(timeout): 回收线程

t.is_alive(): 查看线程状态

t.name: 查看线程名称

threading.currentThread(): 得到线程对象

t.setName(): 设置线程名称

t.daemon=True: 守护线程,默认为False主线程执行完毕不会影响分支线程的执行,True则表示主线程执行完毕其它线程也会终止

设置方法: t.daemon=True 或者 t.setDaemon(True)

t.isDaemon(True): 判断daemon属性是 Trueor False

3.线程属性示例

from threading importThreadfrom threading importcurrentThreadfrom time importsleepdeffunc(sec):print("线程属性测试")

sleep(sec)print("%s线程结束" %currentThread().getName())defmain():

thread=list()for i in range(3):

t= Thread(name="t-" + str(i), target=func, args=(5,))#设置deamon属性为True,此时主线程结束,子线程也会结束

#t.setDaemon(True) # 默认为False,主线程等待子线程结束

print("isDaemon", t.isDaemon())#也可以在定义后设置线程名称

#t.setName = "t-" + str(i)

t.start()

thread.append(t)for i inthread:

i.join(1)print("thread name:", i.name)print("alive:", i.is_alive())print("主线程结束")if __name__ == "__main__":

main()"""执行结果

isDaemon False

线程属性测试

isDaemon False

线程属性测试

isDaemon False

线程属性测试

thread name: t-0

alive: True

thread name: t-1

alive: True

thread name: t-2

alive: True

主线程结束

t-0线程结束

t-2线程结束

t-1线程结束"""

4.线程定时器

from threading import Timer #定时器

deffunc():print("此生一入IT们,从此不爱任何人!")

Timer(2.5, func).start()#Timer(time, func)#time: 睡眠的时间,以秒为单位#func: 睡眠时间之后,需要执行的任务

5.验证守护线程

from threading importThreadimporttimedeffunc():

time.sleep(2)print(123)deffunc1():

time.sleep(1)print("abc")#守护线程不是根据主线程的代码执行结束而结束,而是根据主线程执行结束才结束#主线程会等待普通线程执行结束再结束,守护线程会等待主线程结束再结束,所以一般把不重要的事情设置为守护线程#守护进程是根据主进程的代码执行完毕,守护进程就结束

if __name__ == "__main__":

t= Thread(target=func)

t.daemon=True

t.start()

t1= Thread(target=func1)

t1.start()print(456)

6.自定义线程函数

#将函数作为实参传递给线程类,通过线程类的实例化对象启动线程

importthreadingimporttimedefsing():"""唱歌5秒钟"""

for i in range(5):print("---正在唱歌---%s" %i)

time.sleep(1)defdance():"""跳舞10秒钟"""

for i in range(10):print("---正在跳舞---%s" %i)

time.sleep(1)defmain():#创建线程

t1 = threading.Thread(target=sing)

t2= threading.Thread(target=dance)#启动线程

t1.start()

t2.start()for i in range(15):#查看当前线程数量

print(threading.enumerate())

time.sleep(1)

t1.join()#等待回收线程

t2.join() #等待回收线程

if __name__ == "__main__":

main()

7.自定义线程类

1.继承Thread类重写 run 方法

importthreadingimporttime#threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法#而创建自己的线程实例后,通过Thread类的start方法启动该线程

classMyThread(threading.Thread):defrun(self):for i in range(3):

time.sleep(1)

msg= "I"m"+self.name+"@"+str(i) #name属性中保存的是当前线程的名字

print(msg)if __name__ == "__main__":

t=MyThread()

t.start()

2.继承Thread类重写 run 方法和 __init__ 方法

from threading importThreadfrom time importctimefrom time importsleepclassMyThread(Thread):def __init__(self, func, args, name="Tedu"):

super().__init__()

self.func=func

self.args=args

self.name=namedefrun(self):

self.func(*self.args)

@staticmethod#声明此方法是静态方法

defplary(file, sec):for _ inrange(sec):print("%s: %s" %(file, ctime()))

sleep(sec)defmain():

t= MyThread(MyThread.plary, ("echo", 3))

t.start()

t.join()if __name__ == "__main__":

main()"""echo: Sun Jul 19 23:13:55 2020

echo: Sun Jul 19 23:13:58 2020

echo: Sun Jul 19 23:14:01 2020"""

8.Python线程的全局解释器锁(GIL)

全局解释器锁-产生的原因

GIL是CPython解释器存在的问题

Python支持多线程 同步互斥 加锁 超级锁 但在同一时刻解释器只能解释一个线程

大量的Python库为了省事沿用了这种方法导致了Python多线程效率低下

全局解释器锁-用htop命令查看CPU占有率验证

1.单线程死循环

defmain():#主线程死循环

whileTrue:pass

if __name__ == "__main__":

main()

2.多线程死循环

importthreading#子线程死循环

deftest():pass

defmain():

t1= threading.Thread(target=test)

t1.start()#主线程死循环

whileTrue:pass

if __name__ == "__main__":

main()

3.多进程死循环

importmultiprocessing#子进程死循环

deftest():pass

defmain():

p1= multiprocessing.Process(target=test)

p1.start()#主进程死循环

whileTrue:pass

if __name__ == "__main__":

main()

全局解释器锁-通过计算密集耗时和IO操作耗时验证

importtimeimportthreadingimportmultiprocessing#计算密集

def calculation(num, x=1, y=1):for _ inrange(num):

x+= 1y+= 1

#IO密集-读写

defio_write_read(num):#IO密集-写

f = open("./test.txt", "w")for _ inrange(num):

f.write("hello world!")

f.close()#IO密集-读

f = open("./test.txt", "r")for _ inrange(num):

f.readline()

f.close()#单任务耗时

defsingle_task_time(num, func):

t_start=time.time()for _ in range(10):

func(num)

t_end= time.time() -t_startprint("单线程CPU执行1亿次%s耗时: %s" % (func.__name__, t_end))#多线程耗时

defthreading_time(num, func):

t_start=time.time()

counts=list()for _ in range(10):#t = threading.Thread(target=func, args=(num,), kwargs={"x": 1, "y": 1})

t = threading.Thread(target=func, args=(num,))

t.start()

counts.append(t)for i incounts:

i.join()

t_end= time.time() -t_startprint("10个线程CPU执行1亿次%s耗时: %s" % (func.__name__, t_end))#多进程耗时

defmultiprocessing_time(num, func):

t_start=time.time()

counts=list()for _ in range(10):

p= multiprocessing.Process(target=func, args=(num,))

p.start()

counts.append(p)for i incounts:

i.join()

t_end= time.time() -t_startprint("10个进程CPU执行1亿次%s耗时: %s" % (func.__name__, t_end))defmain():#单线程耗时

single_task_time(5000000, calculation) #单线程CPU执行1亿次calculation耗时: 5.152180910110474

single_task_time(5000000, io_write_read) #单线程CPU执行1亿次io_write_read耗时: 22.120678186416626

#多线程耗时

threading_time(5000000, calculation) #10个线程CPU执行1亿次calculation耗时: 5.205517053604126

threading_time(5000000, io_write_read) #10个线程CPU执行1亿次io_write_read耗时: 22.218310356140137

#多进程耗时

multiprocessing_time(5000000, calculation) #10个进程CPU执行1亿次calculation耗时: 1.7116732597351074

multiprocessing_time(5000000, io_write_read) #10个进程CPU执行1亿次io_write_read耗时: 6.813052177429199

if __name__ == "__main__":

main()"""执行结果

单线程CPU执行1亿次calculation耗时: 5.335744142532349

单线程CPU执行1亿次io_write_read耗时: 21.98353886604309

10个线程CPU执行1亿次calculation耗时: 5.344666814804077

10个线程CPU执行1亿次io_write_read耗时: 22.28637194633484

10个进程CPU执行1亿次calculation耗时: 1.8069920539855957

10个进程CPU执行1亿次io_write_read耗时: 7.141486167907715"""

全局解释器锁-解决方法

1.不使用多线程,使用多进程

2.不使用C C++做的解释器,用 C# Java

3.Python多线程适合高用时的网络IO操作,不适用CPU密集型程序

4.使用其它语言实现多线程,Python去调用

GIL解决方案示例-C语言写子线程执行的函数由Python调用

loop.c文件代码

#include

voidDeadLoop()

{while(1)

{

;

}

}int main(int argc, char **argv)

{

printf("start DeadLoop");

DeadLoop();return 0;

}

命令行下编译C语言写的代码

gcc loop.c  # 编译成功后当前目录先会生成一个a.out的可执行文件

gcc loop.c -shared -o libdead_loop.so  # 执行命令把c语言文件编译成一个动态库文件

./a.out  # 执行文件

Python调用编译后的C语言的动态库文件

importctypesimportthreadingdefmain():#加载动态库

lib = ctypes.cdll.LoadLibrary("libdead_loop.so")#创建一个子线程,让子线程执行C语言写的死循环函数

t = threading.Thread(target=lib.DeadLoop)

t.start()if __name__ == "__main__":

main()

9.多线程-udp聊天器

importsocketimportthreadingdefsend_msg(udp_socket):"""获取键盘数据,并将其发送给对方"""

whileTrue:#1. 从键盘输入数据

msg = input("请输入要发送的数据:")#2. 输入对方的ip地址

dest_ip = input("请输入对方的ip地址:")#3. 输入对方的port

dest_port = int(input("请输入对方的port:"))#4. 发送数据

udp_socket.sendto(msg.encode("utf-8"), (dest_ip, dest_port))defrecv_msg(udp_socket):"""接收数据并显示"""

whileTrue:#1. 接收数据

recv_msg = udp_socket.recvfrom(1024)#2. 解码

recv_ip = recv_msg[1]

recv_msg= recv_msg[0].decode("utf-8")#3. 显示接收到的数据

print(">>>%s:%s" %(str(recv_ip), recv_msg))defmain():#1. 创建套接字

udp_socket =socket.socket(socket.AF_INET, socket.SOCK_DGRAM)#2. 绑定本地信息

udp_socket.bind(("", 7890))#3. 创建一个子线程用来接收数据

t = threading.Thread(target=recv_msg, args=(udp_socket,))

t.start()#4. 让主线程用来检测键盘数据并且发送

send_msg(udp_socket)if __name__ == "__main__":

main()

10.多线程-简单tcp服务器

from socket import *

importosimportsysfrom threading importThreaddefclient_handler(c):try:print("子线程接收%s客户端的请求" %str(c.getpeername()))whileTrue:

data= c.recv(1024)if notdata:break

print(data.decode("utf-8"))

c.send(b"receive your message")except(KeyboardInterrupt, SystemError):raise

exceptException as e:print(e)#关闭客户端套接字

c.close()defmain():#创建套接字

s =socket()#端口重用

s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)#绑定本地信息

s.bind(("", 7890))#监听

s.listen(128)print("主线程%d等待客户端的链接" %os.getpid())whileTrue:try:

c, addr=s.accept()exceptKeyboardInterrupt:raise

exceptException as e:print(e)continuet= Thread(target=client_handler, args=(c,))

t.setDaemon(True)#主线程执行完毕其它线程也会终止

t.start()#关闭监听套接字

s.close()if __name__ == "__main__":

main()

11.多线程-简单tcp服务器的测试客户端

importsocketdefmain():#创建数据流套接字

tcp_client_socket =socket.socket(socket.AF_INET, socket.SOCK_STREAM)#连接服务器

server_ip = input("请输入要连接的服务器的ip:")

serve_port= int(input("请输入要连接的服务器的port:"))

server_addr=(server_ip, serve_port)

tcp_client_socket.connect(server_addr)#发送数据

send_data = input("请输入要发生的数据:")

tcp_client_socket.send(send_data.encode("utf-8"))#接收服务器发送过来的数据

recv_data = tcp_client_socket.recv(1024)print("接收到的数据为:%s" % recv_data.decode("utf-8"))#关闭套接字

tcp_client_socket.close()if __name__ == "__main__":

main()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值