threading
- 继承
threading.thread
- 直接使用
threading.thread
join
等待线程执行结束
下面代码每个任务都是休眠10秒,如果不使用多线程则一共需要30秒,而使用了多线程,只需要10秒。
import time
from threading import Thread
def task1():
print("task1")
time.sleep(10)
print("task1 finish")
def task2():
print("task2")
time.sleep(10)
print("task2 finish")
class MyThread(Thread):
def run(self):
print("mythread")
time.sleep(10)
print("mythread finish")
if __name__ == '__main__':
t = time.time()
# 创建线程的方式1
thread1 = Thread(target=task1)
thread2 = Thread(target=task2)
# 创建线程的方式2
thread3 = MyThread()
thread1.start()
thread2.start()
thread3.start()
# 等待线程结束
thread1.join()
thread2.join()
thread3.join()
print(time.time() - t)
主线程默认等待子线程执行结束,如果主线程想提前结束,则设置为守护子线程
如果主线程执行完了,但是非守护线程还没执行结束,此时,守护线程会继续执行一直到非守护线程结束。
import threading
import time
def task():
for i in range(10):
print(i)
time.sleep(1)
thread = threading.Thread(target=task)
thread.start()
import threading
import time
def task():
while True:
print("child thread")
time.sleep(1)
# 默认主线程会等待所有的子线程都执行才会退出
# 可以设置deamon,标记子线程为守护线程,这样主线程执行结束,子线程自动退出
thread = threading.Thread(target=task, daemon=True)
thread.start()
使用多线程实现socket并发
服务端
import socket
from threading import Thread
'''
a = "hello"
# 字符串转byte
a = bytes(a, encoding="utf-8")
print(a)
a = str(a, encoding="utf-8")
print(a)
'''
# server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 创建socket
server = socket.socket()
# 绑定端口号
server.bind(("0.0.0.0", 8080))
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
server.listen(100)
def do_communicate(conn):
count = 0
while True:
try:
data = conn.recv(1024*1024)
print(len(data))
if len(data) == 0 : break
# print(data.decode("utf-8"))
count += 1
msg = f"收到{count}条消息."
conn.send(msg.encode("utf-8"))
except ConnectionResetError as e:
print(e)
while True:
conn, (ip, addr) = server.accept()
t = Thread(target=do_communicate, args=(conn,))
t.start()
客户端
import socket
import time
if __name__ == "__main__":
client = socket.socket()
client.connect(("127.0.0.1", 8080))
while True:
str = "1234567890"*1024*1024
client.send(str.encode("utf-8"))
time.sleep(1)
data = client.recv(1024)
print(data.decode("utf-8"))
client.close()
线程属性
import time
from threading import Thread,active_count,current_thread
def task():
print("child thread : ", current_thread(), current_thread().name)
time.sleep(1)
if __name__ == '__main__':
t = Thread(target=task)
t.start()
print("active", active_count()) # 当前激活的线程数
print("main thread:", current_thread(), current_thread().name)
互斥锁
如果没有锁,多线程会出现数据错误。
import time
from threading import Thread, active_count, current_thread
ticket = 1000
def sell_ticket():
global ticket
temp = ticket
if temp == 0:
print("没票了")
return
time.sleep(1)
temp -= 1
ticket = temp
print("ticket", ticket)
if __name__ == '__main__':
list = []
for i in range(1000):
t = Thread(target=sell_ticket)
t.start()
list.append(t)
for t in list:
t.join()
print("tick left", ticket)
通过添加互斥锁解决数据错乱的问题
import time
from threading import Thread, Lock
ticket = 1000
mutex = Lock()
def sell_ticket():
global ticket
mutex.acquire()
temp = ticket
if temp == 0:
print("没票了")
return
time.sleep(0.1)
temp -= 1
ticket = temp
print("ticket", ticket)
mutex.release()
if __name__ == '__main__':
list = []
for i in range(1000):
t = Thread(target=sell_ticket)
t.start()
list.append(t)
for t in list:
t.join()
print("tick left", ticket)
GIL全局解释器锁
因为CPyhon解释器的内存管理不是线程安全的,因此会阻止多个线程同时执行代码。
下面代码看起来没问题,但是如果range(1000)改成range(2000)就会出现-1000。但是如果把time.sleep(0.1)
删除。则程序可以正常运行。
import time
from threading import Thread, Lock
ticket = 1000
def sell_ticket():
global ticket
if ticket == 0:
print("没票了")
return
time.sleep(0.1)
ticket-=1
print("ticket", ticket)
if __name__ == '__main__':
list = []
for i in range(1000):
t = Thread(target=sell_ticket)
t.start()
list.append(t)
for t in list:
t.join()
print("tick left", ticket)
递归锁
# 递归锁
# 获取锁的之后,可以继续获取锁
import time
from threading import Thread, RLock
mutext = RLock()
def task1():
mutext.acquire()
mutext.acquire()
time.sleep(1)
print("abc")
mutext.release()
mutext.release()
if __name__ == '__main__':
t1 = Thread(target=task1)
t2 = Thread(target=task1)
t1.start()
t2.start()
信号量
可以设置同时并发数。
from threading import Thread, Semaphore
import time
import random
# sem = Semaphore() # 默认为1
sem = Semaphore(3)
def task(name):
sem.acquire()
print(f"{name} task...")
time.sleep(random.randint(2, 6))
sem.release()
if __name__ == '__main__':
Thread(target=task, args="1").start()
Thread(target=task, args="2").start()
Thread(target=task, args="3").start()
Thread(target=task, args="4").start()
Thread(target=task, args="5").start()
Thread(target=task, args="6").start()
Event
# event事件
import time
from threading import Thread, Event
event = Event()
def task1():
print("task1")
time.sleep(3)
print("task1 finished")
event.set()
def task2():
print("task2")
event.wait() # 等待event通知
print("task2 finished")
if __name__ == '__main__':
Thread(target=task1).start()
Thread(target=task2).start()