目录
一、单任务
二、多任务
三、多任务执行的原理
四、并发、并行
五、线程
六、threading模块---单任务
七、threading模块---多线程实现多任务
八、threading模块---多线程执行相同代码
九、查看线程数量
十、threading模块---创建线程对象时传递参数
10.1一个参数:
10.2 两个参数:
10.3 传递命名参数
十一、自定义类来创建线程
十二、自定义类来创建线程实例----并发TCP服务器
当唱歌执行完了之后再执行跳舞
from time import sleep
def sing():
for i in range(3):
print("正在唱歌。。。。%d" % i)
sleep(1)
def dance():
for i in range(3):
print("正在跳舞。。。%d" % i)
sleep(1)
if __name__ == "__main__":
sing()
dance()
看电影的时候既可以看画面又可以听音频就是多任务。一个人同时唱歌和跳舞就是多任务。
操作系统可以运行多个程序就是多任务的操作系统。如果一个程序是多任务的就是多任务的程序。(也就是说一个程序可以同时执行多个部分的代码)
from time import sleep
import threading
def sing():
for i in range(3):
print("正在唱歌。。。。%d" % i)
sleep(1)
def dance():
for i in range(3):
print("正在跳舞。。。%d" % i)
sleep(1)
if __name__ == "__main__":
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start()
t2.start()
一个cpu是单核默认可以执行一个程序,如果想要多个程序一起执行,理论上就需要多个cpu执行
4核指的是同一时刻有四个任务真正执行。
如果一个cpu是四核,理论上同时只能有4个任务一起执行,但是事实上却可以运行多个程序,之所以有这个现象是因为操作系统控制cpu,让cpu做了一个特殊的事情,一会儿运行一个程序,然后快速的运行另外一个程序,再运行另外的程序,以此类推,实现了多个任务看上去"一起"运行。
操作系统为了让多个程序,都能够得到执行的机会,采用了一系列的方案来实现,例如:时间片调度。
并发:是对一个假的多任务的描述
并行:是真的多任务的描述
32核的服务器:一个cpu有多个核,多个cpu焊在一起的。
任务数大于cpu核数就是假的多任务也就是并发,当任务数小于或者等于cpu核数就是真的多任务。也就是并行。
python中实现多任务有两种:进程和线程
线程:可以想象成程序在执行代码时的那个执行流。执行流可以想象成一个箭头,这个箭头指到哪就执行哪个部分的代码。当创建出多个线程,就可以想象成有多个箭头。多个箭头指向哪就一起执行。
当一个程序运行时,默认有一个线程,这个线程称为主线程。多任务也就是可以理解为让你的代码再运行的过程中额外创建一些线程,让这些线程去执行代码。
python中可以使用线程来实现多任务。可以使用thread模块,但是它比较底层,即意味着过程较为不方便使用;推荐使用threading模块,它是对thread做了一些包装的,可以更加方便使用。
import threading
import time
def say_hello():
print("hello word!!!")
time.sleep(1)
for i in range(5):
t = threading.Thread(target=say_hello)
t.start()
target=say_hello而不是say_hello()。say_hello指的是传入这个函数。而say_hello()指的是传入这个函数的返回值。
import threading
import time
def task_1():
while True:
print(11111)
time.sleep(1)
t1 = threading.Thread(target=task_1)
t1.start()
while True:
print(22222222)
time.sleep(1)
主线程和子线程的顺序不一定。由操作系统决定
import threading
import time
def task_1():
while True:
print(11111)
time.sleep(1)
def task_2():
while True:
print(33333)
time.sleep(0.2)
t1 = threading.Thread(target=task_1)
t2 = threading.Thread(target=task_2)
t1.start()
t2.start()
while True:
print(22222222)
time.sleep(1)
小结:①想要执行一个单独的任务,那么就需要创建一个新的线程,创建出来的线程是不会执行的,只有调用了start方法才会让这个线程开始执行代码。
②一个程序中多个任务一起运行,就想办法创建多个Thread对象。
③多线程执行的顺序是不确定,因为在执行代码的时候,当前的运行环境可能不同以及分配可能不同,导致了操作系统在计算接下来应该调用那个程序的时候得到了不一样的答案,因此顺序不同
import threading
import time
def say_hello():
for i in range(5):
print("hello word!!!")
time.sleep(1)
t1 = threading.Thread(target=say_hello)
t2 = threading.Thread(target=say_hello)
t1.start()
t2.start()
小结:
①一个程序中,可以有多个线程,执行相同的代码,但是每个线程执行每个线程的功能,互不影响,仅仅是做的事情形同罢了。
②当在创建Thread对象是target执行的函数的代码执行完了之后,意味着这个子线程接收。
③虽然主线程没有了代码,但是它依然会等待所有的子线程结束之后,它才会真正的结束,原因是:主线程有个特殊的功能用来对子线程产生的垃圾进行回收处理
④当主线程接收之后,才意味着整个程序真正的结束
import threading
from time import sleep
print(threading.enumerate())
def task1():
for i in range(5):
print(111111)
sleep(1)
t1 = threading.Thread(target=task1)
print(threading.enumerate())
t1.start()
print(threading.enumerate())
sleep(6)
print(threading.enumerate())
小结:
①创建一个Thread对象根本不会创建一个线程,仅仅是一个对象罢了,而当调用它的start方法之后,才会真正创建一个新的子线程
import threading
from time import sleep
def print_lines(num):
for i in range(num):
print("1111")
sleep(0.1)
t = threading.Thread(target=print_lines, args=(5,))
t.start()
import threading
from time import sleep
def print_lines(num, num2):
for i in range(num):
print("1111")
sleep(0.1)
for i in range(num2):
print("2222")
sleep(0.1)
t = threading.Thread(target=print_lines, args=(1, 2))
t.start()
import threading
from time import sleep
def print_lines(num, num2, num3, n):
for i in range(num):
print("1111")
sleep(0.1)
for i in range(num2):
print("2222")
sleep(0.1)
for i in range(num3):
print("3333")
sleep(0.1)
for i in range(n):
print("4444")
sleep(0.1)
t = threading.Thread(target=print_lines, args=(1, 2), kwargs={"n": 4, "num3": 2})
t.start()
import threading
import time
class Task(threading.Thread):
def run(self):
print("1111")
time.sleep(1)
u_t = Task()
u_t.start() # start()去调用run方法
print("main")
time.sleep(1)
运行结果:
小结:
①类里面必须要实现run()方法
②创建的对象调用start()方法。由start()方法来调用子类的run()方法。而不是直接调用run()方法
③run()方法什么时候执行完意味着这个线程什么时候结束
④如果除了run()方法,自定义类里面还有其他方法,这些方法需要在run()方法中自己去调用,线程是不会自己调用的
import socket
import threading
import sys
class HandleData(threading.Thread):
def __init__(self, client_socket): # 需要传入自己的套接字,又需要父类的其他属性
super().__init__()
self.client_socket = client_socket
def run(self):
# 5.接收/发送数据
while True:
recv_content = self.client_socket.recv(1024)
if len(recv_content) != 0:
print(recv_content)
self.client_socket.send(recv_content)
else:
self.client_socket.close()
break
class TCPServer(threading.Thread):
def __init__(self, port):
super().__init__()
# 1.创建套接字
self.server_s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.绑定本地信息
self.server_s.bind(("", port))
# 3.将套接字由默认的主动连接模式改为被动模式(监听模块)
self.server_s.listen(128)
def run(self):
while True:
# 4.等待客户端进行连接。new_s 是新的套接字,client_info是客户端的ip、port
new_s, client_info = self.server_s.accept()
print(client_info)
# 创建一个线程,为刚刚连接的客户端服务
handle_data_thread = HandleData(new_s)
handle_data_thread.start()
def __del__(self):
# 6.关闭套接字
self.server_s.close()
def main():
"""控制整体"""
# python xxx.py 8888
if len(sys.argv) == 2:
port = sys.argv[1]
if port.isdigit():
port = int(port)
else:
print("运行方式如:python xxx.py 8888")
return
print("http服务器使用的port:%s" % port)
tcp_server = TCPServer(port)
tcp_server.start()
if __name__ == "__main__":
main()
小结:
①tcp使用的是套接字,套接字是全双工的。