除了上面的通过Queue队列文件进行数据共享外
还可以使用Manager(list列表,dict字典)进行进程之间的共享数据
from multiprocessing import Process,Manager,Lock
def work(data,lock):
# 1.正常写法
"""
lock.acquire()
# data["count"] -= 1
data[0] += 1
lock.release()
"""
# 2.使用with 语法简化上锁解锁操作
with lock:
data[0] += 1
if __name__ == "__main__":
m = Manager()
# 创建一个共享的字典
data = m.dict( {
"count":20000} )
# 创建一个共享的列表
data = m.list([1,2,3])
# print(data)
lst = []
lock = Lock()
for i in range(100):
p = Process(target=work,args=(data,lock))
p.start()
lst.append(p)
# 确保所有的进程执行完毕,然后在向下运行,打印数据,否则报错;
for i in lst:
i.join()
print(data) # [101, 2, 3]
线程是计算机中调度的最小单位
线程的特点:线程比较轻量级,能干更多的活,一个进程中的所有线程资源是共享的
注:一个进程至少有一个线程在工作
目前配置最高的主机,5万个并发已经是上线
(1)一份进程资源中可以包含多个线程
(2)多线程的速度远远快于多进程
(3)多线程:共享同一份进程资源
(4)用类定义线程:继承线程父类Thread
from threading import Thread
from multiprocessing import Process
import os
# (1)
def func(num):
print('当前线程{},所归属的进程id号{}'.format(os.getpid(),num))
for i in range(10):
# 异步创建10个子线程
t = Thread(target=func,args=(i,))
t.start()
# 主线程执行任务
print(os.getpid())
# (2)
import time
def func(num):
print('当前线程{},所归属的进程id号{}'.format(num, os.getpid()))
if __name__ == "__main__":
starttime = time.time()
lst = []
for i in range(100):
t = Thread(target=func, args=(i,))
t.start()
lst.append(t)
for i in range(100):
p = Process(target=func, args=(i,))
p.start()
lst.append(p)
for i in lst:
i.join()
endtime=time.time()
print("多线程执行时间:", endtime-starttime)
# (3)
num =1000
def func():
global num
num -=1
for i in range(1000):
t=Thread(target=func)
t.start()
print(num)
# (4)
class MyThread(Thread):
def __init__(self,name):
# 手动调用父类的构造方法
super().__init__()
self.name = name
def run(self):
time.sleep(1)
print("当前进程正在执行runing ... " , self.name)
if __name__ == "__main__":
t = MyThread("机器今天会再次爆炸么?")
t.start()
print("主线程执行结束 ... ")
python中的线程可以并发但不能并行;同一个进程下的多个线程不能分开被多个cpu同时执行
原因:
全局解释器锁(Cpython解释器特有)GIL锁:同一时间一个进程下的多个线程只能被一个cpu执行
python是解释型语言,不能一次性全部编译成功,不能提前规划,都是临时调度,容易造成cpu执行调度异常,所以加了一把锁叫GIL
实现并行的方法:
(1)用多进程间接实现线程的并行
(2)换一个Pypy,Jpython解释器
线程分为计算密集型和IO密集型:
计算密集型会大量占用cpu资源,对于python来说负担大
IO密集型像网页,爬虫,OA办公,python处理起来就很方便
(1)is_alive():检测线程是否仍然存在
(2)setName():设置线程名
(3)getName():获取线程名
(4)currentThread().ident:查看线程id号
(5)enumerate():返回目前正在运行的线程列表
(6)activeCount():返回目前正在运行的线程数量
#
def func():
time.sleep(1)
if __name__ == "__main__":
t = Thread(target=func)
t.start()
print(t.is_alive()) # False
print(t.getName())
t.setName("1234")
print(t.getName())
# currentThread().ident
def func():
print("子线程id",currentThread().ident , os.getpid())
if __name__ == "__main__":
Thread(target=func).start()
print("主线程id",currentThread().ident , os.getpid())
# enumerate(),activeCount()
from threading import enumerate
from threading import activeCount # (了解)
def func():
print("子线程id",currentThread().ident , os.getpid())
time.sleep(0.5)
if __name__ == "__main__":
for i in range(10):
Thread(target=func).start()
lst = enumerate()
print( activeCount() )
注:等待所有线程全部执行完,才终止程序,守护的是所有线程
from threading import Thread
import time
def func1():
while True:
time.sleep(0.5)
print("我是func1")
def func2():
print("我是func2 start ... ")
time.sleep(3)
print("我是func2 end ... ")
t1 = Thread(target=func1)
t2 = Thread(target=func2)
# 在start调用之前,设置守护线程
t1.setDaemon(True)
t1.start()
t2.start()
print("主线程执行结束 ... ")
Lock和信号量Semaphore
保证线程数据安全
注:在创建线程的时候是异步创建,在执行的时候因为Semaphore加了锁,所以线程之间变成同步
# Lock
from threading import Lock,Thread
import time
n = 0
def func1(lock):
global n
lock.acquire()
for i in range(1000000):
# 方法一
n -= 1
lock.release()
def func2(lock):
global n
# with 自动完成上锁+解锁
with lock:
for i in range(1000000):
# 方法二
n += 1
if __name__ == "__main__":
lst = []
lock = Lock()
time1 = time.time()
for i in range(10):
t1 = Thread(target=func1,args=(lock,))
t2 = Thread(target=func2,args=(lock,))
t1.start()
t2.start()
lst.append(t1)
lst.append(t2)
# 等待所有的子线程执行结束之后, 在打印数据
for i in lst:
i.join()
time2 = time.time()
print("主线程执行结束..." , n ,time2 - time1)
# semaphore
from threading import Semaphore,Thread
import time
def func(i,sm):
with sm:
print(i)
time.sleep(3)
if __name__ == "__main__":
sm = Semaphore(5)
for i in range(20):
Thread(target=func,args=(i,sm)).start()