目录
第1关:求素数的个数
import math
from multiprocessing import cpu_count
from multiprocessing import Pool
'''使用python多进程 求区间[1,n]里 素数个数'''
def isPrime(n):
# 判断数字是否为素数
# 请在此处添加代码 #
# *************begin************#
if n<=1:
return 0
if n==2:
return 1
if n % 2 ==0: #毛:注意此行必写,否则没有考虑到2这个数,循环从3开始,忽略了2
return 0
for i in range(3, int(math.sqrt(n)) + 1,2):
if n%i == 0:
return 0
return 1
# **************end*************#
def howMany(T):
# 计算给定区间含有多少个素数
# 请在此处添加代码 #
# *************begin************#
sum = 0
for i in range(T[0],T[1]+1):
if isPrime(i):
sum+=1
return sum
# **************end*************#
def separateNum(N, CPU_COUNT):
# 将整个数字空间N分成CPU_COUNT个部分
# 请在此处添加代码 #
# *************begin************#
#使用列表推导式生成一个二维列表list
#每个子列包含CPU_COUNT个子列表,每个子列表包含2个元素:
#起始数字:i*(N//CPU_COUNT)+1
#结束数字:(i+1)*(N//CPU_COUNT)
list = [[i * (N//CPU_COUNT)+1, (i+1)*(N // CPU_COUNT)] for i in range(0,CPU_COUNT)]
list[0][0] =1
if list[CPU_COUNT - 1][1] <N:
list[CPU_COUNT - 1][1] = N
return list
# **************end*************#
if __name__ == '__main__':
N = int(input()) #输入一个整数N(区间[1,N]中的素数个数)
CPU_COUNT = cpu_count() #获取当前计算机CPU内核数 本机为8,存储在CPU_COUNT中
pool = Pool(CPU_COUNT) #创建一个进程池,使用CPU_COUNT个进程
sepList = separateNum(N, CPU_COUNT) #区间[1,N]分成CPU_COUNT个部分,存储在列表seqList中
result = []#创建空列表result,存储异步任务结果
for i in range(CPU_COUNT):
result.append(pool.apply_async(howMany, (sepList[i], ))) #启动异步进程,计算个数,存储列表中
pool.close()#关闭进程池,等待所有进程执行完毕
pool.join() #使用列表推导式将result列表中的结果提取出来,存储在列表list中
list = [res.get() for res in result]
print(sum(list), end = '')#计算素数和,输出
素数优化:设置从3开始,步长为2 ,进行优化
第2关:求合数的个数
import threading
import math
ans = 0
lock = threading.Lock()
def isPrime(n):
# 判断数字是否为素数
global ans
if n <= 1:
return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
return True
def howMany(T):
# 计算给定区间含有多少个素数
sum = 0
for i in range(T[0], T[1] + 1):
if isPrime(i):
sum += 1
lock.acquire()
try:
global ans
ans += sum
finally:
lock.release()
def seprateNum(N, CPU_COUNT):
# 对整个数字空间N进行分段CPU_COUNT
list = [[i * (N // CPU_COUNT) + 1, (i + 1) * (N // CPU_COUNT)] for i in range(0, CPU_COUNT)]
list[0][0] = 1
if list[CPU_COUNT - 1][1] < N:
list[CPU_COUNT - 1][1] = N
return list
if __name__ == '__main__': #当前脚本是主程序
N = int(input()) #获取用户输入的整数,转化为整型,赋值给变量N
threadNum = 32 #线程数设为32
# *************begin************#
t = [] #定义一个线程列表t
sepList = seprateNum(N,threadNum)#将整数N划分为threadNum个部分
for i in range(0,threadNum): #生成线程,存储在列表t中
t.append(threading.Thread(target = howMany,args = (sepList[i],))) #将函数howmany作为线程的执行函数,传入sepList[i]中作为参数
t[i].start() #启动线程
for i in range(0,threadNum): #等所有线程执行结束
t[i].join()
print(N-1-ans,end='') #计算ans的值,将N-1-ans的值输出,不换行
#ans:满足要求的数的个数,剩余数个数
# **************end*************#
第3关:交替打印foobarpython
import threading
import sys
import time
def showfoo(n):
'''
:param n: 要输出foobarpython的次数
:return: 无返回,可直接输出
'''
# *************begin************#
#注意缩进:这个for空4个格子
for i in range(n): #循环n次
lockpython.acquire() #获取锁,保证线程同步,锁名为:lockpython
print('foo',end='') #输出'foo',确保不换行
sys.stdout.flush() #刷新输出缓存
lockfoo.release() #释放锁(保证线程同步),锁名为lockfoo
time.sleep(0.2) #线程休眠0.2S
# **************end*************#
def showbar(n):
'''
:param n: 要输出foobarpython的次数
:return: 无返回,可直接输出
'''
# *************begin************#
for i in range(n):
lockfoo.acquire()
print('bar',end='')
sys.stdout.flush()
lockbar.release()
time.sleep(0.2)
# **************end*************#
def showpython(n):
'''
:param n: 要输出foobarpython的次数
:return: 无返回,可直接输出
'''
# 请在此处添加代码 #
# *************begin************#
for i in range(n):
lockbar.acquire()
print('python',end='')
sys.stdout.flush()
lockpython.release()
time.sleep(0.2)
# **************end*************#
if __name__ == '__main__':
lockfoo = threading.Lock() # 定义3个互斥锁
lockbar = threading.Lock()
lockpython =threading.Lock()
n = int(input())
t1 = threading.Thread(target=showfoo,args=[n]) # 定义3个线程
t2 = threading.Thread(target=showbar,args=[n])
t3 = threading.Thread(target=showpython,args=[n])
lockfoo.acquire() # 先锁住foo,bar锁,保证先打印foo
lockbar.acquire()
#lockpython.acquire()
t1.start()
t2.start()
t3.start()
关于第一第二题思考:
(一)起始数字+结束数字 如何判断?
i代表当前正在处理的部分的索引,从0到CPU_COUNT-1。在每个循环中,我们使用i计算当前部分的起始数字和结束数字,具体地:
起始数字:i * (N // CPU_COUNT) + 1,这是因为第一个部分的起始数字应该是1,而每个部分包含的数字个数是N // CPU_COUNT,因此第i个部分的起始数字应该是(i * (N // CPU_COUNT)) + 1。
结束数字:(i + 1) * (N // CPU_COUNT),这是因为第i个部分的结束数字应该比起始数字多包含(N // CPU_COUNT)个数字,而第(i+1)个部分的起始数字应该是第i个部分的结束数字+1,因此第i个部分的结束数字应该是(i+1) * (N // CPU_COUNT) - 1 + 1,即(i+1) * (N // CPU_COUNT)。
举个例子,如果N=100,CPU_COUNT=4,那么我们要将数字1~100分为4个部分,每个部分包含的数字个数是25。
对于第0个部分,其起始数字为0 * 25 + 1 = 1,结束数字为(0+1) * 25 = 25。
对于第1个部分,其起始数字为1 * 25 + 1 = 26,结束数字为(1+1) * 25 = 50。
对于第2个部分,其起始数字为2 * 25 + 1 = 51,结束数字为(2+1) * 25 = 75。
对于第3个部分,其起始数字为3 * 25 + 1 = 76,结束数字为(3+1) * 25 = 100。
因此,二维列表list的每个子列表都包含了一个部分的起始数字和结束数字。
(二)main函数执行的代码功能
首先判断当前文件是否作为主程序运行,如果是则执行下面的代码,如果不是则不执行。
1、输入一个整数N,表示要求区间[1,N]中的素数个数。
2、获取当前计算机的CPU核心数,存储在变量CPU_COUNT中。
3、创建一个进程池对象pool,使用CPU_COUNT个进程。
4、将区间[1,N]分成CPU_COUNT个部分,存储在列表sepList中。
5、创建一个空列表result,用于存储异步任务的结果。
使用pool.apply_async()方法对每个子区间sepList[i]启动一个异步进程,计算该区间内的素数个数,并将结果存储在result列表中。
6、关闭进程池,等待所有进程执行完毕。
使用列表推导式将result列表中的结果提取出来,存储在列表list中。
计算所有子区间内的素数个数之和,将结果输出。
(三)光轮老师提出的素数优化代码:
关于第三题思考:
(一)Await作用
await 操作符用于等待一个 Promise 兑现并获取它兑现之后的值。它只能在异步函数或者模块顶层中使用。
(二)关于锁的机制和作用:
我们需要控制三个线程的执行顺序,保证每个线程在执行一次后,只有另外一个线程才能开始执行。所以我们需要使用锁机制来实现线程同步。
在showfoo函数中,我们需要保证先打印’foo’,因此在函数开始时,先使用lockpython.acquire()来锁住python锁,保证showpython函数不会先执行。然后在打印完’foo’后,使用lockfoo.release()来释放foo锁,让showbar函数可以执行。
在showbar函数中,我们需要保证先打印’bar’,因此在函数开始时,先使用lockfoo.acquire()来锁住foo锁,保证showfoo函数已经执行完毕。然后在打印完’bar’后,使用lockbar.release()来释放bar锁,让showpython函数可以执行。
在showpython函数中,我们需要保证先打印’python’,因此在函数开始时,先使用lockbar.acquire()来锁住bar锁,保证showbar函数已经执行完毕。然后在打印完’python’后,使用lockpython.release()来释放python锁,让showfoo函数可以执行。
这三个锁的作用就是控制三个函数的执行顺序,保证每个函数执行一次后,只有另外一个函数才能执行。