මම උත්සාහ කරන්නේ පයිතන්හි නූල් තේරුම් ගැනීමටයි. මම ලියකියවිලි සහ උදාහරණ දෙස බැලුවෙමි, නමුත් අවංකවම, බොහෝ උදාහරණ ඕනෑවට වඩා නවීන වන අතර ඒවා තේරුම් ගැනීමට මට අපහසුය.
බහු-නූල් දැමීම සඳහා කාර්යයන් බෙදා ඇති බව ඔබ පැහැදිලිව පෙන්වන්නේ කෙසේද?
මම උත්සාහ කරන්නේ පයිතන්හි නූල් තේරුම් ගැනීමටයි. මම ලියකියවිලි සහ උදාහරණ දෙස බැලුවෙමි, නමුත් අවංකවම, බොහෝ උදාහරණ ඕනෑවට වඩා නවීන වන අතර ඒවා තේරුම් ගැනීමට මට අපහසුය.
බහු-නූල් දැමීම සඳහා කාර්යයන් බෙදා ඇති බව ඔබ පැහැදිලිව පෙන්වන්නේ කෙසේද?
Answers:
මෙම ප්රශ්නය 2010 දී ඇසූ දා සිට, සිතියම සහ තටාකය සමඟ පයිතන් සමඟ සරල බහුකාර්යයක් කරන්නේ කෙසේද යන්න පිළිබඳ සැබෑ සරල කිරීමක් සිදුවී ඇත .
පහත කේතය පැමිණෙන්නේ ඔබ අනිවාර්යයෙන්ම පරීක්ෂා කර බැලිය යුතු ලිපියක් / බ්ලොග් සටහනකිනි (අනුබද්ධයක් නොමැත) - එක පේළියක සමාන්තරකරණය: එදිනෙදා නූල් දැමීමේ කාර්යයන් සඳහා වඩා හොඳ ආකෘතියක් . මම පහත සාරාංශගත කරමි - එය අවසන් වන්නේ කේත පේළි කිහිපයක් පමණි:
from multiprocessing.dummy import Pool as ThreadPool
pool = ThreadPool(4)
results = pool.map(my_function, my_array)
කුමන බහුකාර්ය අනුවාදය:
results = []
for item in my_array:
results.append(my_function(item))
විස්තර
සිතියම සිසිල් කුඩා කාර්යයක් වන අතර ඔබේ පයිතන් කේතයට සමාන්තරකරණය පහසුවෙන් එන්නත් කිරීමේ යතුරයි. නුහුරු අය සඳහා, සිතියම යනු ලිස්ප් වැනි ක්රියාකාරී භාෂාවලින් ඉවත් කළ දෙයකි. එය අනුක්රමයක් හරහා තවත් ශ්රිතයක් සිතියම් ගත කරන ශ්රිතයකි.
සිතියම අප සඳහා අනුක්රමය හරහා පුනරාවර්තනය හසුරුවයි, ශ්රිතය අදාළ කරයි, සහ සියලු ප්රති results ල අවසානයේ හුරුබුහුටි ලැයිස්තුවක ගබඩා කරයි.
ක්රියාත්මක කිරීම
සිතියම් ශ්රිතයේ සමාන්තර අනුවාදයන් පුස්තකාල දෙකක් මඟින් සපයනු ලැබේ: බහු සැකසුම්, සහ එහි එතරම් ප්රසිද්ධ නැති, නමුත් ඒ හා සමානවම අපූරු පියවර දරුවා: multirocessing.dummy.
multiprocessing.dummy
බහු සැකසුම් මොඩියුලයට හරියටම සමාන ය, නමුත් ඒ වෙනුවට නූල් භාවිතා කරයි ( වැදගත් වෙනසක් - CPU- තීව්ර කාර්යයන් සඳහා බහු ක්රියාදාමයන් භාවිතා කරන්න; I / O සඳහා (සහ අතරතුර) නූල් :
multirocessing.dummy බහුකාර්යකරණයේ API ප්රතිවර්තනය කරයි, නමුත් එය නූල් මොඩියුලය වටා එතීමට වඩා වැඩි නොවේ.
import urllib2
from multiprocessing.dummy import Pool as ThreadPool
urls = [
'http://www.python.org',
'http://www.python.org/about/',
'http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html',
'http://www.python.org/doc/',
'http://www.python.org/download/',
'http://www.python.org/getit/',
'http://www.python.org/community/',
'https://wiki.python.org/moin/',
]
# Make the Pool of workers
pool = ThreadPool(4)
# Open the URLs in their own threads
# and return the results
results = pool.map(urllib2.urlopen, urls)
# Close the pool and wait for the work to finish
pool.close()
pool.join()
සහ කාල ප්රති results ල:
Single thread: 14.4 seconds
4 Pool: 3.1 seconds
8 Pool: 1.4 seconds
13 Pool: 1.3 seconds
බහුවිධ තර්ක පසු කිරීම (මේ වගේ ක්රියා කරන්නේ පයිතන් 3.3 සහ ඊට පසුව පමණි ):
බහු අරා පසු කිරීමට:
results = pool.starmap(function, zip(list_a, list_b))
හෝ නියත හා අරාවක් පසු කිරීමට:
results = pool.starmap(function, zip(itertools.repeat(constant), list_a))
ඔබ පයිතන්ගේ පෙර සංස්කරණයක් භාවිතා කරන්නේ නම්, ඔබට මෙම ක්රියාකාරීත්වය හරහා විවිධ තර්ක ඉදිරිපත් කළ හැකිය ).
( ප්රයෝජනවත් අදහස් දැක්වීම සඳහා පරිශීලක 136036 ට ස්තූතියි .)
with Pool(8) as p: p.map( *whatever* )
පොත් තැබීමේ රේඛා ලිවීමට හා ඉවත් කිරීමට හැකිය.
මෙන්න සරල උදාහරණයකි: ඔබට විකල්ප URL කිහිපයක් උත්සාහ කර ප්රතිචාර දැක්වීමට පළමු එකෙහි අන්තර්ගතය නැවත ලබා දිය යුතුය.
import Queue
import threading
import urllib2
# Called by each thread
def get_url(q, url):
q.put(urllib2.urlopen(url).read())
theurls = ["http://google.com", "http://yahoo.com"]
q = Queue.Queue()
for u in theurls:
t = threading.Thread(target=get_url, args = (q,u))
t.daemon = True
t.start()
s = q.get()
print s
මෙය සරල ප්රශස්තිකරණයක් ලෙස නූල් භාවිතා කරන අවස්ථාවකි: සෑම උපප්රස්ථාරයක්ම එහි අන්තර්ගතය පෝලිමේ තැබීම සඳහා URL එකක් නිරාකරණය කර ප්රතිචාර දැක්වීමට බලා සිටී; සෑම නූල් එකක්ම ඩීමන් වේ (ප්රධාන නූල් අවසන් වුවහොත් ක්රියාවලිය දිගටම පවතින්නේ නැත - එය වඩා පොදු නොවේ); ප්රධාන නූල් සියළුම උපප්රස්ථාර ආරම්භ කරයි, get
ඒවායින් එකක් සිදු වන තුරු පෝලිමේ රැඳී සිටින්නput
විමෝචනය කර අවසන් වේ (එමඟින් ඩීමන් නූල් බැවින් ඒවා තවමත් ක්රියාත්මක විය හැකි ඕනෑම උපප්රස්ථාරයක් ඉවත් කරයි).
පයිතන් හි නූල් නිසි ලෙස භාවිතා කිරීම නිරන්තරයෙන් I / O මෙහෙයුම් සමඟ සම්බන්ධ වේ (කෙසේ හෝ CPU- බැඳී ඇති කාර්යයන් ක්රියාත්මක කිරීම සඳහා CPython බහු හරයන් භාවිතා නොකරන බැවින්, නූල් දැමීමේ එකම හේතුව ක්රියාවලිය අවහිර නොකිරීම සහ සමහර I / O සඳහා රැඳී සිටීමයි. ). පෝලිම් නිරන්තරයෙන් පාහේ නූල් වලට වැඩ කිරීමට සහ / හෝ වැඩ ප්රති results ල එක්රැස් කිරීමට හොඳම ක්රමය වන අතර, ඒවා සහජයෙන්ම නූල් ආරක්ෂිතයි, එබැවින් ඒවා අගුල්, කොන්දේසි, සිදුවීම්, සෙමෆෝර් සහ වෙනත් අන්තර් ගැන කරදර වීමෙන් ඔබව ගලවා ගනී. තුන්වන සම්බන්ධීකරණය / සන්නිවේදන සංකල්ප.
join()
වනුයේ ක්රමවේදය භාවිතා කිරීම යැයි මම සිතමි. එමඟින් ප්රධාන ත්රෙඩ් එක නිරන්තරයෙන් සකසනය පරිභෝජනය නොකර ඒවා අවසන් වන තෙක් බලා සිටිනු ඇත. වටිනාකම පරීක්ෂා කිරීම. Lex ඇලෙක්ස්: ස්තූතියි, නූල් භාවිතා කරන්නේ කෙසේද යන්න තේරුම් ගැනීමට මට අවශ්ය වූයේ මෙයයි.
Queue
මොඩියුලයේ නම ආදේශ කරන්න queue
. ක්රමයේ නම සමාන වේ.
s = q.get()
print s
rs join
krs013 Queue.get () අවහිර වන නිසා ඔබට අවශ්ය නොවේ .
සටහන : පයිතන්හි සත්ය සමාන්තරකරණය සඳහා, ඔබ බහු සැකසුම් භාවිතා කළ යුතුය නිසා ගෝලීය භාෂණ අගුළු (සමගාමීව ක්රියාත්මක බව බහු සැකසුම් දෙබලක කිරීමට මොඩියුලය, Python නූල් interleaving ලබා, නමුත් ඔවුන් ඇත්තෙන්ම නැහැ සමගාමීව serially ක්රියාත්මක කර ඇති අතර, පමණක් I / O මෙහෙයුම් අන්තර් සම්බන්ධ කිරීමේදී ප්රයෝජනවත් වේ).
කෙසේ වෙතත්, ඔබ හුදෙක් අන්තර් සම්බන්ධතා සොයන්නේ නම් (හෝ ගෝලීය පරිවර්තක අගුල තිබියදීත් සමාන්තරගත කළ හැකි I / O මෙහෙයුම් කරන්නේ නම්), නූල් මොඩියුලය ආරම්භ කළ යුතු ස්ථානයයි. සැබවින්ම සරල උදාහරණයක් ලෙස, උප පරාසයන් සමාන්තරව කැඳවීමෙන් විශාල පරාසයක් සාරාංශ කිරීමේ ගැටළුව සලකා බලමු:
import threading
class SummingThread(threading.Thread):
def __init__(self,low,high):
super(SummingThread, self).__init__()
self.low=low
self.high=high
self.total=0
def run(self):
for i in range(self.low,self.high):
self.total+=i
thread1 = SummingThread(0,500000)
thread2 = SummingThread(500000,1000000)
thread1.start() # This actually causes the thread to run
thread2.start()
thread1.join() # This waits until the thread has completed
thread2.join()
# At this point, both threads have completed
result = thread1.total + thread2.total
print result
ඉහත දැක්වෙන්නේ ඉතා මෝඩ උදාහරණයකි, මන්ද එය නියත වශයෙන්ම I / O නොමැති අතර ගෝලීය පරිවර්තක අගුල හේතුවෙන් CPython හි අන්තර් සම්බන්ධිත (සන්දර්භය මාරුවීමේ අමතර පිරිවැය සමඟ) අනුක්රමිකව ක්රියාත්මක කරනු ඇත .
thread1
ප්රධාන නූල් අවහිර වන තෙක් එය ක්රියාත්මක වන තෙක් ක්රියාත්මක වන අතර පසුව එකම දේ සිදු වේ thread2
, ඉන්පසු ප්රධාන නූල් නැවත ආරම්භ වී ඒවා රැස් කළ අගයන් මුද්රණය කරයි.
super(SummingThread, self).__init__()
නොවේද? Stackoverflow.com/a/2197625/806988
සඳහන් කළ අනෙක් අය මෙන්, CPYthon හට නූල් භාවිතා කළ හැක්කේ GIL නිසා I / O රැඳී සිටීම සඳහා පමණි .
ඔබට භාවිත CPU-බැඳී කාර්යයන් සඳහා බහු මධ්යය ප්රයෝජන ගැනීමට අවශ්ය නම් multiprocessing :
from multiprocessing import Process
def f(name):
print 'hello', name
if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
p.join()
f
ක්රියාකාරිත්වය. සමාන්තරව, ප්රධාන වැඩසටහන දැන් ක්රියාවලියෙන් ඉවත්ව යන join
තෙක් බලා සිටී. ප්රධාන කොටස දැන් පිටවී ඇත්නම්, උප ක්රියාවලිය සම්පුර්ණ වීමට හෝ නොවීමට ඉඩ ඇත, එබැවින් එය join
කිරීම සැමවිටම නිර්දේශ කෙරේ.
map
ශ්රිතය ඇතුළත් පුළුල් පිළිතුරක් මෙහි ඇත: stackoverflow.com/a/28463266/2327328
සටහනක් පමණක්: නූල් දැමීම සඳහා පෝලිමක් අවශ්ය නොවේ.
ක්රියාවලි 10 ක් සමගාමීව ක්රියාත්මක වන බව මට සිතාගත හැකි සරලම උදාහරණය මෙයයි.
import threading
from random import randint
from time import sleep
def print_number(number):
# Sleeps a random 1 to 10 seconds
rand_int_var = randint(1, 10)
sleep(rand_int_var)
print "Thread " + str(number) + " slept for " + str(rand_int_var) + " seconds"
thread_list = []
for i in range(1, 10):
# Instantiates the thread
# (i) does not make a sequence, so (i,)
t = threading.Thread(target=print_number, args=(i,))
# Sticks the thread in a list so that it remains accessible
thread_list.append(t)
# Starts threads
for thread in thread_list:
thread.start()
# This blocks the calling thread until the thread whose join() method is called is terminated.
# From http://docs.python.org/2/library/threading.html#thread-objects
for thread in thread_list:
thread.join()
# Demonstrates that the main process waited for threads to complete
print "Done"
for
පුඩුවක් අවශ්ය නොවේ , ඔබට thread.start()
පළමු පුඩුවෙන් ඇමතිය හැකිය .
ඇලෙක්ස් මාටෙලිගෙන් පිළිතුර මට උදව් විය. කෙසේ වෙතත්, මෙන්න වඩා ප්රයෝජනවත් යැයි මා සිතූ නවීකරණය කරන ලද අනුවාදයකි (අවම වශයෙන් මට).
යාවත්කාලීන කරන ලද්දේ: පයිතන් 2 සහ පයිතන් 3 යන දෙකෙහිම ක්රියා කරයි
try:
# For Python 3
import queue
from urllib.request import urlopen
except:
# For Python 2
import Queue as queue
from urllib2 import urlopen
import threading
worker_data = ['http://google.com', 'http://yahoo.com', 'http://bing.com']
# Load up a queue with your data. This will handle locking
q = queue.Queue()
for url in worker_data:
q.put(url)
# Define a worker function
def worker(url_queue):
queue_full = True
while queue_full:
try:
# Get your data off the queue, and do some work
url = url_queue.get(False)
data = urlopen(url).read()
print(len(data))
except queue.Empty:
queue_full = False
# Create as many threads as you want
thread_count = 5
for i in range(thread_count):
t = threading.Thread(target=worker, args = (q,))
t.start()
import Queue ModuleNotFoundError: No module named 'Queue'
මම පයිතන් 3.6.5 ධාවනය කරන බව සමහර පෝස්ට් වල සඳහන් වන්නේ පයිතන් 3.6.5 හි එය පවතින queue
නමුත් මම එය වෙනස් කළත් තවමත් ක්රියා නොකරයි
ශ්රිතයක් ලබා දී f
, මේ ආකාරයට නූල් කරන්න:
import threading
threading.Thread(target=f).start()
වෙත තර්ක ඉදිරිපත් කිරීමට f
threading.Thread(target=f, args=(a,b,c)).start()
Thread
වස්තුව පිරිසිදු වේ. ලියකියවිලි බලන්න . පවතින අතර මෙය is_alive()
ඔබට අවශ්ය නම්, ඔබ නූල් පරීක්ෂා කිරීමට භාවිතා කළ හැකි ක්රමය.
is_alive
ක්රමය දුටුවෙමි , නමුත් එය නූල් එකට යොදන ආකාරය මට සිතාගත නොහැකි විය. මම එය පැවරීමට උත්සාහ thread1=threading.Thread(target=f).start()
කර එය පරීක්ෂා කර බැලුවෙමි thread1.is_alive()
, නමුත් thread1
ජනාකීර්ණ None
බැවින් එහි කිසිදු වාසනාවක් නොමැත. නූල් වලට ප්රවේශ වීමට වෙනත් ක්රමයක් තිබේදැයි ඔබ දන්නවාද?
thread1=threading.Thread(target=f)
ඉන්පසු thread1.start()
. එවිට ඔබට කළ හැකිය thread1.is_alive()
.
thread1.is_alive()
ප්රතිලාභ සමඟ පරීක්ෂා කිරීම False
.
මෙය ඉතා ප්රයෝජනවත් බව මට පෙනී ගියේය: හරයන් තරම් නූල් සාදන්න සහ විශාල (විශාල) කාර්යයන් ප්රමාණයක් ක්රියාත්මක කිරීමට ඔවුන්ට ඉඩ දෙන්න (මේ අවස්ථාවේ දී, ෂෙල් වැඩසටහනක් කැඳවීම):
import Queue
import threading
import multiprocessing
import subprocess
q = Queue.Queue()
for i in range(30): # Put 30 tasks in the queue
q.put(i)
def worker():
while True:
item = q.get()
# Execute a task: call a shell program and wait until it completes
subprocess.call("echo " + str(item), shell=True)
q.task_done()
cpus = multiprocessing.cpu_count() # Detect number of cores
print("Creating %d threads" % cpus)
for i in range(cpus):
t = threading.Thread(target=worker)
t.daemon = True
t.start()
q.join() # Block until all tasks are done
පයිතන් 3 සමාන්තර කාර්යයන් දියත් කිරීමේ පහසුකම ඇත . මෙය අපගේ කාර්යය පහසු කරයි.
එහි නූල් සංචලනය සහ ක්රියාවලි සංචලනය ඇත ඇත.
පහත දැක්වෙන්නේ තීක්ෂ්ණ බුද්ධියකි:
ThreadPoolExecutor උදාහරණය ( මූලාශ්රය )
import concurrent.futures
import urllib.request
URLS = ['http://www.foxnews.com/',
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/',
'http://some-made-up-domain.com/']
# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
with urllib.request.urlopen(url, timeout=timeout) as conn:
return conn.read()
# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
# Start the load operations and mark each future with its URL
future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
for future in concurrent.futures.as_completed(future_to_url):
url = future_to_url[future]
try:
data = future.result()
except Exception as exc:
print('%r generated an exception: %s' % (url, exc))
else:
print('%r page is %d bytes' % (url, len(data)))
ProcessPoolExecutor ( ප්රභවය )
import concurrent.futures
import math
PRIMES = [
112272535095293,
112582705942171,
112272535095293,
115280095190773,
115797848077099,
1099726899285419]
def is_prime(n):
if n % 2 == 0:
return False
sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n + 1, 2):
if n % i == 0:
return False
return True
def main():
with concurrent.futures.ProcessPoolExecutor() as executor:
for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
print('%d is prime: %s' % (number, prime))
if __name__ == '__main__':
main()
දැවෙන නව සමගාමී. අනාගත මොඩියුලය භාවිතා කිරීම
def sqr(val):
import time
time.sleep(0.1)
return val * val
def process_result(result):
print(result)
def process_these_asap(tasks):
import concurrent.futures
with concurrent.futures.ProcessPoolExecutor() as executor:
futures = []
for task in tasks:
futures.append(executor.submit(sqr, task))
for future in concurrent.futures.as_completed(futures):
process_result(future.result())
# Or instead of all this just do:
# results = executor.map(sqr, tasks)
# list(map(process_result, results))
def main():
tasks = list(range(10))
print('Processing {} tasks'.format(len(tasks)))
process_these_asap(tasks)
print('Done')
return 0
if __name__ == '__main__':
import sys
sys.exit(main())
ක්රියාකරුගේ ප්රවේශය මීට පෙර ජාවා සමඟ අපිරිසිදු දෑත් ඇති සියල්ලන්ට හුරු පුරුදු බවක් පෙනෙන්නට තිබේ.
පැත්තක සටහනක: විශ්වය සන්සුන්ව තබා ගැනීමට, ඔබ with
සන්දර්භය භාවිතා නොකරන්නේ නම් ඔබේ තටාක / ක්රියාකරුවන් වසා දැමීමට අමතක නොකරන්න (එය ඔබ වෙනුවෙන් කරන තරමට නියමයි)
මට නම්, නූල් දැමීම සඳහා හොඳම උදාහරණය වන්නේ අසමමුහුර්ත සිදුවීම් නිරීක්ෂණය කිරීමයි. මෙම කේතය දෙස බලන්න.
# thread_test.py
import threading
import time
class Monitor(threading.Thread):
def __init__(self, mon):
threading.Thread.__init__(self)
self.mon = mon
def run(self):
while True:
if self.mon[0] == 2:
print "Mon = 2"
self.mon[0] = 3;
IPython සැසියක් විවෘත කර ඔබට එවැනි කේතයක් සමඟ සෙල්ලම් කළ හැකිය :
>>> from thread_test import Monitor
>>> a = [0]
>>> mon = Monitor(a)
>>> mon.start()
>>> a[0] = 2
Mon = 2
>>>a[0] = 2
Mon = 2
මිනිත්තු කිහිපයක් ඉන්න
>>> a[0] = 2
Mon = 2
බොහෝ ලියකියවිලි සහ නිබන්ධන භාවිතා කරන්නේ පයිතන්ගේ Threading
සහQueue
මොඩියුලය අතර ඒවා ආරම්භකයින් සඳහා අති විශාල බවක් පෙනේ.
සමහර විට සලකා බලන්න concurrent.futures.ThreadPoolExecutor
පයිතන් 3 මොඩියුලය .
with
වගන්තිය හා ලැයිස්තු අවබෝධය සමඟ ඒකාබද්ධව එය සැබෑ චමත්කාරයක් විය හැකිය.
from concurrent.futures import ThreadPoolExecutor, as_completed
def get_url(url):
# Your actual program here. Using threading.Lock() if necessary
return ""
# List of URLs to fetch
urls = ["url1", "url2"]
with ThreadPoolExecutor(max_workers = 5) as executor:
# Create threads
futures = {executor.submit(get_url, url) for url in urls}
# as_completed() gives you the threads once finished
for f in as_completed(futures):
# Get the results
rs = f.result()
සැබෑ කාර්යයන් සිදු නොකෙරෙන උදාහරණ රාශියක් මම මෙහි දුටුවෙමි, ඒවා බොහෝ දුරට CPU වලට බැඳී ඇත. මිලියන 10 ත් මිලියන 10.05 ත් අතර සියලු ප්රාථමික සංඛ්යා ගණනය කරන CPU- බැඳී ඇති කාර්යයක උදාහරණයක් මෙන්න. මම මෙහි ක්රම හතරම භාවිතා කර ඇත:
import math
import timeit
import threading
import multiprocessing
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def time_stuff(fn):
"""
Measure time of execution of a function
"""
def wrapper(*args, **kwargs):
t0 = timeit.default_timer()
fn(*args, **kwargs)
t1 = timeit.default_timer()
print("{} seconds".format(t1 - t0))
return wrapper
def find_primes_in(nmin, nmax):
"""
Compute a list of prime numbers between the given minimum and maximum arguments
"""
primes = []
# Loop from minimum to maximum
for current in range(nmin, nmax + 1):
# Take the square root of the current number
sqrt_n = int(math.sqrt(current))
found = False
# Check if the any number from 2 to the square root + 1 divides the current numnber under consideration
for number in range(2, sqrt_n + 1):
# If divisible we have found a factor, hence this is not a prime number, lets move to the next one
if current % number == 0:
found = True
break
# If not divisible, add this number to the list of primes that we have found so far
if not found:
primes.append(current)
# I am merely printing the length of the array containing all the primes, but feel free to do what you want
print(len(primes))
@time_stuff
def sequential_prime_finder(nmin, nmax):
"""
Use the main process and main thread to compute everything in this case
"""
find_primes_in(nmin, nmax)
@time_stuff
def threading_prime_finder(nmin, nmax):
"""
If the minimum is 1000 and the maximum is 2000 and we have four workers,
1000 - 1250 to worker 1
1250 - 1500 to worker 2
1500 - 1750 to worker 3
1750 - 2000 to worker 4
so let’s split the minimum and maximum values according to the number of workers
"""
nrange = nmax - nmin
threads = []
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
# Start the thread with the minimum and maximum split up to compute
# Parallel computation will not work here due to the GIL since this is a CPU-bound task
t = threading.Thread(target = find_primes_in, args = (start, end))
threads.append(t)
t.start()
# Don’t forget to wait for the threads to finish
for t in threads:
t.join()
@time_stuff
def processing_prime_finder(nmin, nmax):
"""
Split the minimum, maximum interval similar to the threading method above, but use processes this time
"""
nrange = nmax - nmin
processes = []
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
p = multiprocessing.Process(target = find_primes_in, args = (start, end))
processes.append(p)
p.start()
for p in processes:
p.join()
@time_stuff
def thread_executor_prime_finder(nmin, nmax):
"""
Split the min max interval similar to the threading method, but use a thread pool executor this time.
This method is slightly faster than using pure threading as the pools manage threads more efficiently.
This method is still slow due to the GIL limitations since we are doing a CPU-bound task.
"""
nrange = nmax - nmin
with ThreadPoolExecutor(max_workers = 8) as e:
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
e.submit(find_primes_in, start, end)
@time_stuff
def process_executor_prime_finder(nmin, nmax):
"""
Split the min max interval similar to the threading method, but use the process pool executor.
This is the fastest method recorded so far as it manages process efficiently + overcomes GIL limitations.
RECOMMENDED METHOD FOR CPU-BOUND TASKS
"""
nrange = nmax - nmin
with ProcessPoolExecutor(max_workers = 8) as e:
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
e.submit(find_primes_in, start, end)
def main():
nmin = int(1e7)
nmax = int(1.05e7)
print("Sequential Prime Finder Starting")
sequential_prime_finder(nmin, nmax)
print("Threading Prime Finder Starting")
threading_prime_finder(nmin, nmax)
print("Processing Prime Finder Starting")
processing_prime_finder(nmin, nmax)
print("Thread Executor Prime Finder Starting")
thread_executor_prime_finder(nmin, nmax)
print("Process Executor Finder Starting")
process_executor_prime_finder(nmin, nmax)
main()
මෙන්න මගේ මැක් ඕඑස් එක්ස් ෆෝ-කෝර් යන්ත්රයේ ප්රති results ල
Sequential Prime Finder Starting
9.708213827005238 seconds
Threading Prime Finder Starting
9.81836523200036 seconds
Processing Prime Finder Starting
3.2467174359990167 seconds
Thread Executor Prime Finder Starting
10.228896902000997 seconds
Process Executor Finder Starting
2.656402041000547 seconds
if __name__ == '__main__':
ප්රධාන ඇමතුමක්, වෙනත් ආකාරයකින් මිනුම් spawns ම පෙර සහ මුද්රණය කරයි දැරූ උත්සාහය පෙර ... නව ක්රියාවලිය ආරම්භ කිරීමට සැලසුම් කර ඇත .
CSV සඳහා ඉතා සරල උදාහරණය මෙන්නනූල් භාවිතා කරමින් ආනයනය . (පුස්තකාල ඇතුළත් කිරීම විවිධ අරමුණු සඳහා වෙනස් විය හැකිය.)
උපකාරක කාර්යයන්:
from threading import Thread
from project import app
import csv
def import_handler(csv_file_name):
thr = Thread(target=dump_async_csv_data, args=[csv_file_name])
thr.start()
def dump_async_csv_data(csv_file_name):
with app.app_context():
with open(csv_file_name) as File:
reader = csv.DictReader(File)
for row in reader:
# DB operation/query
ධාවක ක්රියාකාරිත්වය:
import_handler(csv_file_name)
මම සරල උදාහරණයකින් දායක වීමට කැමැත්තෙමි. මෙම ගැටලුව මා විසින්ම විසඳා ගැනීමට මට සිදු වූ විට පැහැදිලි කිරීම් ප්රයෝජනවත් විය.
මෙම පිළිතුරෙන් ඔබට පයිතන්ගේ ජීඅයිඑල් (ගෝලීය පරිවර්තක අගුල) පිළිබඳ තොරතුරු සහ බහු ප්රොසෙසින්.ඩම්මි සහ සරල මිණුම් සලකුණු භාවිතා කර ලියා ඇති සරල එදිනෙදා උදාහරණයක් සොයාගත හැකිය .
ගෝලීය පරිවර්තක අගුල (GIL)
වචනයේ සත්ය අර්ථයෙන් පයිතන් බහු-නූල් දැමීමට ඉඩ නොදේ. එයට බහු-නූල් පැකේජයක් ඇත, නමුත් ඔබේ කේතය වේගවත් කිරීම සඳහා ඔබට බහු-නූල් කිරීමට අවශ්ය නම්, එය සාමාන්යයෙන් එය භාවිතා කිරීම හොඳ අදහසක් නොවේ.
පයිතන්ට ගෝලීය පරිවර්තක අගුල (GIL) නමින් ඉදිකිරීම් ඇත. ඕනෑම වේලාවක ක්රියාත්මක කළ හැක්කේ ඔබගේ 'නූල්' වලින් එකක් පමණක් බව GIL සහතික කරයි. නූල් එකක් GIL ලබා ගනී, සුළු වැඩක් කරයි, පසුව GIL ඊළඟ නූල් වෙත යොමු කරයි.
මෙය ඉතා ඉක්මණින් සිදු වන නිසා මිනිස් ඇසට ඔබේ නූල් සමාන්තරව ක්රියාත්මක වන බවක් පෙනෙන්නට තිබුණත් ඒවා ඇත්ත වශයෙන්ම එකම CPU හරය භාවිතා කරමින් හැරීම් සිදු කරයි.
මේ සියල්ලම GIL පසු කිරීම ක්රියාත්මක කිරීමට ඉහළින් එකතු කරයි. මෙයින් අදහස් කරන්නේ ඔබේ කේතය වේගයෙන් ක්රියාත්මක කිරීමට අවශ්ය නම් නූල් පැකේජය භාවිතා කිරීම බොහෝ විට හොඳ අදහසක් නොවන බවයි.
පයිතන්ගේ නූල් පැකේජය භාවිතා කිරීමට හේතු තිබේ. ඔබට එකවර සමහර දේවල් ක්රියාත්මක කිරීමට අවශ්ය නම්, සහ කාර්යක්ෂමතාව සැලකිලිමත් නොවේ නම්, එය මුළුමනින්ම කදිම සහ පහසුය. නැතහොත් ඔබ යම් දෙයක් සඳහා බලා සිටිය යුතු කේතයක් ධාවනය කරන්නේ නම් (සමහර I / O වැනි) එවිට එය බොහෝ අර්ථවත් කළ හැකිය. නමුත් නූල් පුස්තකාලය මඟින් ඔබට අමතර CPU හර භාවිතා කිරීමට ඉඩ නොදේ.
බහු-නූල් මෙහෙයුම් පද්ධතියට බාහිරින් ලබා ගත හැකිය (බහු සැකසුම් කිරීමෙන්), සහ ඔබේ පයිතන් කේතය (උදාහරණයක් ලෙස ස්පාර්ක් හෝ හැඩූප් ) ලෙස හඳුන්වන සමහර බාහිර යෙදුම් හෝ ඔබේ පයිතන් කේතය ඇමතීමේ සමහර කේත (උදාහරණයක් ලෙස: ඔබට හැකි විය ඔබේ පයිතන් කේතය මිල අධික බහු-නූල් දේවල් කරන C ශ්රිතයක් අමතන්න).
ඇයි මේ වැදගත්
GIL යනු කුමක්දැයි ඉගෙන ගැනීමට පෙර බොහෝ අය ඔවුන්ගේ විසිතුරු පයිතන් බහු-නූල් කේතයේ ඇති බාධක සොයා ගැනීමට බොහෝ කාලයක් වැය කරන බැවිනි.
මෙම තොරතුරු පැහැදිලි වූ පසු, මෙන්න මගේ කේතය:
#!/bin/python
from multiprocessing.dummy import Pool
from subprocess import PIPE,Popen
import time
import os
# In the variable pool_size we define the "parallelness".
# For CPU-bound tasks, it doesn't make sense to create more Pool processes
# than you have cores to run them on.
#
# On the other hand, if you are using I/O-bound tasks, it may make sense
# to create a quite a few more Pool processes than cores, since the processes
# will probably spend most their time blocked (waiting for I/O to complete).
pool_size = 8
def do_ping(ip):
if os.name == 'nt':
print ("Using Windows Ping to " + ip)
proc = Popen(['ping', ip], stdout=PIPE)
return proc.communicate()[0]
else:
print ("Using Linux / Unix Ping to " + ip)
proc = Popen(['ping', ip, '-c', '4'], stdout=PIPE)
return proc.communicate()[0]
os.system('cls' if os.name=='nt' else 'clear')
print ("Running using threads\n")
start_time = time.time()
pool = Pool(pool_size)
website_names = ["www.google.com","www.facebook.com","www.pinterest.com","www.microsoft.com"]
result = {}
for website_name in website_names:
result[website_name] = pool.apply_async(do_ping, args=(website_name,))
pool.close()
pool.join()
print ("\n--- Execution took {} seconds ---".format((time.time() - start_time)))
# Now we do the same without threading, just to compare time
print ("\nRunning NOT using threads\n")
start_time = time.time()
for website_name in website_names:
do_ping(website_name)
print ("\n--- Execution took {} seconds ---".format((time.time() - start_time)))
# Here's one way to print the final output from the threads
output = {}
for key, value in result.items():
output[key] = value.get()
print ("\nOutput aggregated in a Dictionary:")
print (output)
print ("\n")
print ("\nPretty printed output: ")
for key, value in output.items():
print (key + "\n")
print (value)
මෙන්න සරල උදාහරණයක් සහිත බහු නූල් කිරීම ප්රයෝජනවත් වනු ඇත. ඔබට එය ක්රියාත්මක කළ හැකි අතර පයිතන් හි බහු නූල් ක්රියා කරන ආකාරය පහසුවෙන් තේරුම් ගත හැකිය. පෙර නූල් ඒවායේ වැඩ අවසන් වන තුරු වෙනත් නූල් වලට ප්රවේශ වීම වැළැක්වීම සඳහා මම අගුලක් භාවිතා කළෙමි. මෙම කේත රේඛාව භාවිතා කිරීමෙන්,
tLock = threading.BoundedSemaphore (අගය = 4)
ඔබට වරකට ක්රියාවලි ගණනාවකට ඉඩ දිය හැකි අතර පෙර ක්රියාදාමයන් පසුව හෝ පසුව ක්රියාත්මක වන ඉතිරි නූල් රඳවා තබා ගන්න.
import threading
import time
#tLock = threading.Lock()
tLock = threading.BoundedSemaphore(value=4)
def timer(name, delay, repeat):
print "\r\nTimer: ", name, " Started"
tLock.acquire()
print "\r\n", name, " has the acquired the lock"
while repeat > 0:
time.sleep(delay)
print "\r\n", name, ": ", str(time.ctime(time.time()))
repeat -= 1
print "\r\n", name, " is releaseing the lock"
tLock.release()
print "\r\nTimer: ", name, " Completed"
def Main():
t1 = threading.Thread(target=timer, args=("Timer1", 2, 5))
t2 = threading.Thread(target=timer, args=("Timer2", 3, 5))
t3 = threading.Thread(target=timer, args=("Timer3", 4, 5))
t4 = threading.Thread(target=timer, args=("Timer4", 5, 5))
t5 = threading.Thread(target=timer, args=("Timer5", 0.1, 5))
t1.start()
t2.start()
t3.start()
t4.start()
t5.start()
print "\r\nMain Complete"
if __name__ == "__main__":
Main()
මෙම තනතුරෙන් ණයට ගැනීමත් සමඟ බහු-කියවීම, බහු සැකසුම් සහ අසින්ක් / asyncio
සහ ඒවායේ භාවිතය අතර තෝරා ගැනීම ගැන අපි දනිමු .
සමගාමී හා සමාන්තරකරණය සඳහා පයිතන් 3 නව පුස්තකාලයක් ඇත: concurrent.futures
එබැවින් මම කාර්යයන් හතරක් (එනම් .sleep()
ක්රමය) ක්රියාත්මක කිරීම සඳහා අත්හදා බැලීමක් මඟින් නිරූපණය කරමි Threading-Pool
:
from concurrent.futures import ThreadPoolExecutor, as_completed
from time import sleep, time
def concurrent(max_worker):
futures = []
tic = time()
with ThreadPoolExecutor(max_workers=max_worker) as executor:
futures.append(executor.submit(sleep, 2)) # Two seconds sleep
futures.append(executor.submit(sleep, 1))
futures.append(executor.submit(sleep, 7))
futures.append(executor.submit(sleep, 3))
for future in as_completed(futures):
if future.result() is not None:
print(future.result())
print(f'Total elapsed time by {max_worker} workers:', time()-tic)
concurrent(5)
concurrent(4)
concurrent(3)
concurrent(2)
concurrent(1)
ප්රතිදානය:
Total elapsed time by 5 workers: 7.007831811904907
Total elapsed time by 4 workers: 7.007944107055664
Total elapsed time by 3 workers: 7.003149509429932
Total elapsed time by 2 workers: 8.004627466201782
Total elapsed time by 1 workers: 13.013478994369507
[ සටහන ]:
multiprocessing
එදිරිව threading
ඔබ වෙනස් විය හැකි) ThreadPoolExecutor
වෙත ProcessPoolExecutor
.පෙර විසඳුම් කිසිවක් මගේ ග්නූ / ලිනක්ස් සේවාදායකයේ බහු හර භාවිතා කර නැත (මට පරිපාලක අයිතිවාසිකම් නොමැති තැන). ඔවුන් තනි හරයක් මත දිව ගියා.
os.fork
බහුවිධ ක්රියාදාමයන් ඇති කිරීමට මම පහළ මට්ටමේ අතුරු මුහුණත භාවිතා කළෙමි . මෙය මා වෙනුවෙන් වැඩ කළ කේතයයි:
from os import fork
values = ['different', 'values', 'for', 'threads']
for i in range(len(values)):
p = fork()
if p == 0:
my_function(values[i])
break
import threading
import requests
def send():
r = requests.get('https://www.stackoverlow.com')
thread = []
t = threading.Thread(target=send())
thread.append(t)
t.start()