文章目录
多任务
import threading
def dance():
for i in range(50):
print('我正在跳舞')
def sing():
for i in range(50):
print('我正在唱歌')
# 多个任务同时执行
# Python里执行多任务:多线程、多进程、多进程+多线程
dance()
sing()
# target 需要的是一个函数,用来指定线程需要执行的任务
t1 = threading.Thread(target=dance()) # 创建了线程1
t2 = threading.Thread(target=sing()) # 创建了线程2
# 启动线程
t1.start()
t2.start()
二、线程
2.1 多线程聊天
import socket, threading
s = socket.socket(AF_INET, socket.SPCK_DGRAM)
s.bind(('192.168.31.180', 9090))
def send_msg():
while True:
content = input('请输入要发生的信息:')
s.sendto(content.encode('utf8'),('192.168.31.199', 9000))
def rec_msg():
while True:
content = s.recvfrom(1024).decode('utf8')
print(content)
t1 = threading.Thread(target=send_msg) # 创建线程1
t2 = threading.Thread(target=rec_msg) # 创建线程2
t1.start()
t2.start()
2.2 多个线程共享全局变量
import threading
# 多个线程可以同时操作一个全局变量(多个线程共享全局变量)
# 线程安全问题
ticket = 20
def sell_ticket():
global ticket
while True:
if ticket > 0:
ticket -= 1
print(f'{threading.current_thread().name}卖出一张票,还剩{ticket}张')
else:
print('票买完了')
break
t1 = threading.Thread(target=sell_ticket, name='线程1')
t2 = threading.Thread(target=sell_ticket, name='线程2')
t1.start()
t2.start()
2.3 线程锁
当多个线程几乎同时修改某一个共享数据的时候,需要进行 同步控制。同步就是协同步调,按预定的先后次序进行运行。线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制就是引入互斥锁
import threading
# 多个线程可以同时操作一个全局变量(多个线程共享全局变量)
# 线程安全问题
ticket = 20
# 创建一把锁
lock = threading.Lock()
def sell_ticket():
global ticket
while True:
lock.acquire() # 加同步锁
if ticket > 0:
ticket -= 1
print(f'{threading.current_thread().name}卖出一张票,还剩{ticket}张')
lock.release()
else:
lock.release()
print('票买完了')
break
t1 = threading.Thread(target=sell_ticket, name='线程1')
t2 = threading.Thread(target=sell_ticket, name='线程2')
t1.start()
t2.start()
2.4 线程间通信
import threading, queue
def produce():
for i in range(10):
p.put(f'b{i}')
print(f'{threading.current_thread().name}生产了面包 b{i}')
def consumer()
for i in range(100):
# q.get() 方法是一个阻塞的方法,即队列内没有数据则一直等待至有数据为止
print(f'{threading.current_thread().name}消费了面包{q.get()}')
q = queue.Queue() # 创建一个q
p1 = threading.Thread(target=produce, name = 'p1')
c1 = threading.Thread(target=consumer, name = 'c1')
p1.start()
c1.start()
三、进程
3.1 进程简介
程序:例如 XXX.py 这是程序,是一个静态的
进程:一个程序运行起来后,代码+用到的资源 称之为进程,他是操作系统分配资源的基本单元。
不仅可以通过线程完成多任务,进程也是可以的
3.2 进程的状态
工作中,任务数往往大于cpu的核数,即一定有一些任务可以执行,而另外一些任务在等待cpu进行执行,因此导致了有了不同的状态
- 就绪态:运行的条件都已经满足,正在等在cpu执行
- 执行态:cpu正在执行其功能
- 等待态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态
3.3 多进程
import multiprocessing, os
def dance(n):
for i in range(n):
print(f'正在跳舞{i},pid={os.getpid()}')
def sing(m):
for i in range(m):
print(f'正在唱歌{i},pid={os.getpid()}')
if __name__ = '__main__':
# 创建了两个进程
# target 用来表示执行的任务
# args 用来传参,类型是一个元组
p1 = multiprocessing.Process(target=dance, args=(20,))
p2 = multiprocessing.Process(target=sing, args=(50,))
p1.dance()
p2.sing()
3.4 线程和进程的区别
-
功能
- 进程:能够完成多任务,比如 在一台电脑上运行多个qq
- 线程:能够完成多任务,比如 一个QQ中的多个聊天窗口
-
定义不同
- 进程是系统进行资源分配和调度的一个独立单位
- 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源
-
区别
- 一个程序至少有一个进程,一个进程至少有一个线程
- 线程的划分尺度小于进程(资源比进程少),使得多线程程序并发性高
- 进程在执行过程中拥有独立的内存单元,而多线程共享内存,从而极大地提高了程序的运行效率
- 线程不能单独执行,必须依存在进程中
- 可以将进程理解为工厂的一条流水线,而其中的线程就是这个流水线上的工人
-
优缺点
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反
3.5 进程不共享全局变量
import os, threading, multiprocessing
n = 100
def test():
global n
n += 1
print('{os.getpid()} n的值是{n}')
def demo():
global n
n += 1
print('{os.getpid()} n的值是{n}')
# test() # 101
# test() # 102
# 同一个主进程里的两个子线程。线程之间可以共享同一进程的全局变量
# t1 = threading.Thread(target=test)
# t2 = threading.Thread(target=demo)
# t1.start()
# t2.start()
if __name__ = '__main__':
# 不同进程各自保存一份全局变量,不会共享全局变量
p1 = multiprocessing.Process(target=test)
p2 = multiprocessing.Process(target=demo)
p1.start() #
p2.start() #
3.6 进程间通信
import os, multiprocessing
def producer(q):
for i in range(10):
print(f'生产了+++++pid{os.getpid()} {i}')
q.put(f'pid{os.getpid()} {i}')
def consumer(q):
for i in range(10):
print(f'消费了---------{q.get()}')
if __name__ == '__main__':
q = multiprocessing.Queue()
p1 = multiprocessing.Process(target=producer, args=(q,))
c1 = multiprocessing.Process(target=consumer,args=(q,))
p1.start()
c1.start()
3.7 queue队列
import multiprocessing, queue
# q1 = multiprocessing.Queue() # 进程间通信
# q2 = queue.Queue() # 线程间通信
# 创建队列时,可以指定最大长度。默认值是0,表示不限
q = multiprocessing.Queue(5)
q.put('a')
q.put('b')
q.put('c')
q.put('d')
q.put('e')
# print(q.full()) # True 判断当前队列是否满队列
# q.put('f') # 无法放进去,会阻塞当前线程
# block = True:表示是阻塞,如果队列已经满了,就等待
# timeout 超时,等待多久以后程序会出错,单位是秒
# q.put('how', block=True)
q.put_nowait('how') # 等价于 q.put('how',block=False)
print(q.get()) # a
print(q.get()) # b
print(q.get()) # c
print(q.get()) # d
print(q.get()) # e
# q.get() # 默认队列取空了会阻塞等待
# q.get(block=True, timeout = 10)
q.get_nowait() # 等价于q.get(block=False)
3.8 进程池
当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程,但如果是上百个甚至上千个目标,手动的取创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法
初始化Pool时,可以指定一个最大进程数,当有新的骑牛提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务
from multiprocessing import Pool
import os, time, random
def worker(msg):
t_start = time.time()
print(f"{msg}开始执行,进程号为{os.getpid()}")
# random.random()随机生成0~1之间的浮点数
time.sleep(random.random() * 2)
t_stop = time.time()
print(f"{msg} 执行完毕,耗时{t_stop - s_start}")
if __name__ = '__main__'
po = Pool(3) # 定义一个进程池,最大进程数3
for i in range(0, 10):
# Pool().apply_async(要调用的目标,(传递给目标的参数元组,))
# 每次循环将会用进程池中空闲出来的子进程去调用目标
po.apply_async(worker,(i,))
print("----start----")
po.close() # 关闭进程池,关闭后 po 不在接收新的请求
3.9 join方法的使用
# join 线程和进程都有 join方法
import time
x = 10
def test(a, b):
time.sleep(1)
global x
x = a + b
# test(1, 1)
# print(x) # 2
t = threading.Thread(target=test,args=(1,1))
t.start()
t.join() # 让主线程等待子线程执行完毕
print(x) # 若没有 t.join() 则为10,否则为 2