সমান্তরালভাবে ফাংশনগুলি কীভাবে চালানো যায়?


118

আমি প্রথমে গবেষণা করেছিলাম এবং আমার প্রশ্নের উত্তর খুঁজে পাইনি। আমি পাইথনের সমান্তরালে একাধিক ফাংশন চালানোর চেষ্টা করছি।

আমার এরকম কিছু রয়েছে:

files.py

import common #common is a util class that handles all the IO stuff

dir1 = 'C:\folder1'
dir2 = 'C:\folder2'
filename = 'test.txt'
addFiles = [25, 5, 15, 35, 45, 25, 5, 15, 35, 45]

def func1():
   c = common.Common()
   for i in range(len(addFiles)):
       c.createFiles(addFiles[i], filename, dir1)
       c.getFiles(dir1)
       time.sleep(10)
       c.removeFiles(addFiles[i], dir1)
       c.getFiles(dir1)

def func2():
   c = common.Common()
   for i in range(len(addFiles)):
       c.createFiles(addFiles[i], filename, dir2)
       c.getFiles(dir2)
       time.sleep(10)
       c.removeFiles(addFiles[i], dir2)
       c.getFiles(dir2)

আমি ফানক 1 এবং ফান 2 কে কল করতে চাই এবং সেগুলি একই সাথে চালাতে চাই। ফাংশনগুলি একে অপরের সাথে বা একই বস্তুর সাথে যোগাযোগ করে না। এখনই আমাকে ফানক 2 শুরু হওয়ার আগে ফানক 1 এর সমাপ্তির জন্য অপেক্ষা করতে হবে। নীচের মতো আমি কীভাবে করব:

process.py

from files import func1, func2

runBothFunc(func1(), func2())

আমি একই সময়ে খুব কাছাকাছি উভয় ডিরেক্টরি তৈরি করতে সক্ষম হতে চাই কারণ প্রতি মিনিটে আমি কতগুলি ফাইল তৈরি হচ্ছে তা গণনা করছি। ডিরেক্টরিটি যদি না থাকে তবে এটি আমার সময় ছুঁড়ে দেবে।


4
আপনি এটি পুনরায় স্থপতি করতে চাইবেন; যদি আপনি প্রতি মিনিটে ফাইল / ফোল্ডারগুলির সংখ্যা গণনা করছেন তবে আপনি একটি রেসের শর্ত তৈরি করছেন। উভয় ফাংশন সম্পাদন শেষ না হওয়া পর্যন্ত পর্যায়ক্রমিক প্রক্রিয়া গণনা আপডেট করে না তা নিশ্চিত করার জন্য প্রতিটি ফাংশনটির একটি কাউন্টার আপডেট হওয়া বা একটি লকফিল ব্যবহার করার কী আছে?

উত্তর:


178

আপনি ব্যবহার করতে পারেন threadingবা multiprocessing

সিপথনের অদ্ভুততার কারণে , threadingপ্রকৃত সমান্তরালতা অর্জনের সম্ভাবনা কম is এই কারণে, multiprocessingসাধারণত একটি ভাল বাজি।

এখানে একটি সম্পূর্ণ উদাহরণ:

from multiprocessing import Process

def func1():
  print 'func1: starting'
  for i in xrange(10000000): pass
  print 'func1: finishing'

def func2():
  print 'func2: starting'
  for i in xrange(10000000): pass
  print 'func2: finishing'

if __name__ == '__main__':
  p1 = Process(target=func1)
  p1.start()
  p2 = Process(target=func2)
  p2.start()
  p1.join()
  p2.join()

শিশু প্রক্রিয়াগুলি শুরু / যোগদানের মেকানিক্সগুলি আপনার লাইনের সাথে সহজেই কোনও ফাংশনে আবদ্ধ হতে পারে runBothFunc:

def runInParallel(*fns):
  proc = []
  for fn in fns:
    p = Process(target=fn)
    p.start()
    proc.append(p)
  for p in proc:
    p.join()

runInParallel(func1, func2)

4
আমি আপনার কোড ব্যবহার করেছি তবে ফাংশনগুলি এখনও একই সময়ে শুরু হয়নি।
lmcadory

4
@ লামার ম্যাকএডরি: দয়া করে আপনি "একই সাথে" বলতে কী বোঝাতে চেয়েছেন তা ব্যাখ্যা করুন, সম্ভবত আপনি কী করেছেন, আপনি কী প্রত্যাশা করেছিলেন এবং আসলে কী হয়েছিল তার একটি নিখুঁত উদাহরণ প্রদান করে।
এনপিই

4
@ লামার: আপনার কাছে কখনই "ঠিক একই সময়" এর কোনও গ্যারান্টি থাকতে পারে না এবং আপনি ভাবতে পারেন যে এটি কেবল সাধারণ ভুল। আপনার কতটি সিপাস রয়েছে তার উপর নির্ভর করে, মেশিনের লোড, কম্পিউটারে ঘটে যাওয়া অনেক কিছুর টাইমিংয়ের থ্রেড / প্রক্রিয়া শুরু হওয়ার সময় সমস্তটিরই প্রভাব থাকবে। এছাড়াও, যেহেতু প্রক্রিয়াগুলি তৈরির ঠিক পরে শুরু হয়েছিল, তাই কোনও প্রক্রিয়া তৈরির ওভারহেডটি আপনি যে সময়ের পার্থক্য দেখেন তাতেও গণনা করতে হবে।
মার্টিন

4
প্রতিটি ফাংশনের ফলাফলের তালিকা পাওয়া কি সম্ভব? যাক প্রতিটি ফাংশন একটি আলাদা মান ফেরত দেয়, মানগুলি পরে ব্যবহার করা যেতে পারে এমন কিছু তালিকায় যুক্ত হতে পারে? সম্ভবত একটি বিশ্ব তালিকায় ফলাফল সংযোজন?
pelos

4
যদি আমার ফাংশনগুলি প্যারামিটার নেয় এবং আমি পৃথক প্রক্রিয়াগুলি থেকে কল করার সময় প্যারামিটারগুলি পাস করি তবে তারা একসাথে চালায় না। আপনি দয়া করে সহায়তা করতে পারেন
user2910372

21

এই সঙ্গে এইরূপ সূচারূভাবে সম্পন্ন করা যাবে রে , একটি সিস্টেম আপনি সহজেই parallelize এবং আপনার পাইথন কোডটি বিতরণ করতে পারবেন।

আপনার উদাহরণটির সমান্তরাল করতে আপনার @ray.remoteসাজসজ্জারের সাথে আপনার ফাংশনগুলি সংজ্ঞায়িত করতে হবে এবং তারপরে তাদের সাথে অনুরোধ করতে হবে .remote

import ray

ray.init()

dir1 = 'C:\\folder1'
dir2 = 'C:\\folder2'
filename = 'test.txt'
addFiles = [25, 5, 15, 35, 45, 25, 5, 15, 35, 45]

# Define the functions. 
# You need to pass every global variable used by the function as an argument.
# This is needed because each remote function runs in a different process,
# and thus it does not have access to the global variables defined in 
# the current process.
@ray.remote
def func1(filename, addFiles, dir):
    # func1() code here...

@ray.remote
def func2(filename, addFiles, dir):
    # func2() code here...

# Start two tasks in the background and wait for them to finish.
ray.get([func1.remote(filename, addFiles, dir1), func2.remote(filename, addFiles, dir2)]) 

যদি আপনি উভয় ফাংশনে একই যুক্তিটি পাস করেন এবং তর্কটি বড় হয় তবে এটি করার আরও কার্যকর উপায় ব্যবহার করা হচ্ছে ray.put()। এটি বৃহত যুক্তিটিকে দু'বার সিরিয়ালাইজ করা এবং এর দুটি মেমরি অনুলিপি তৈরি করতে এড়িয়ে চলে:

largeData_id = ray.put(largeData)

ray.get([func1(largeData_id), func2(largeData_id)])

গুরুত্বপূর্ণ - যদি func1()এবং func2()ফলাফলগুলি ফিরে আসে, আপনাকে নীচে কোডটি পুনরায় লিখতে হবে:

ret_id1 = func1.remote(filename, addFiles, dir1)
ret_id2 = func2.remote(filename, addFiles, dir2)
ret1, ret2 = ray.get([ret_id1, ret_id2])

মাল্টিপ্রসেসিং মডিউলটির সাহায্যে রে ব্যবহারের বিভিন্ন সুবিধা রয়েছে । বিশেষত, একই কোডটি একটি একক মেশিনের পাশাপাশি মেশিনগুলির একটি গোষ্ঠীতে চলবে। রায়ের আরও সুবিধার জন্য এই সম্পর্কিত পোস্টটি দেখুন


আমি এটিকে সর্বোত্তম বিকল্প বলে মনে করেছি। আমি এটিতে একটি জিনিস যুক্ত করব, বিশেষত আপনি যদি এটি ডকারে ব্যবহার করেন তবে তা হ'ল এটি আর্কিটেকচার নির্ভর। এই মুহুর্তে, আলপাইন লিনাক্সে কাজ করবেন না (সেন্টোস 7 আমার জন্য কাজ করেছে) এবং আপনার এটি চালানোর ray.shutdown()পরে দৌড়াতে হবে কারণ আপনি যা করছেন তা কোনওভাবেই জটিল হলে আপনার স্মৃতি দ্রুত চলে যাবে।
jimh

19

যদি আপনার ফাংশনগুলি প্রধানত I / O কাজ করে (এবং কম সিপিইউ কাজ করে) এবং আপনার পাইথন ৩.২++ থাকে তবে আপনি একটি থ্রেডপুলএক্সেকিউটার ব্যবহার করতে পারেন :

from concurrent.futures import ThreadPoolExecutor

def run_io_tasks_in_parallel(tasks):
    with ThreadPoolExecutor() as executor:
        running_tasks = [executor.submit(task) for task in tasks]
        for running_task in running_tasks:
            running_task.result()

run_io_tasks_in_parallel([
    lambda: print('IO task 1 running!'),
    lambda: print('IO task 2 running!'),
])

যদি আপনার ফাংশনগুলি মূলত সিপিইউ কাজ করে (এবং কম আই / ও কাজ করে) এবং আপনার পাইথন ২.6++ রয়েছে তবে আপনি মাল্টিপ্রসেসিং মডিউলটি ব্যবহার করতে পারেন :

from multiprocessing import Process

def run_cpu_tasks_in_parallel(tasks):
    running_tasks = [Process(target=task) for task in tasks]
    for running_task in running_tasks:
        running_task.start()
    for running_task in running_tasks:
        running_task.join()

run_cpu_tasks_in_parallel([
    lambda: print('CPU task 1 running!'),
    lambda: print('CPU task 2 running!'),
])

এটি একটি ভাল উত্তর। সমাপ্তি.ফিউচার কোনটি সম্পন্ন করে আই / ও বাউন্ড কাজের জন্য ফলাফলটি কীভাবে সনাক্ত করবেন? মূলত লাম্বা ফাংশনগুলির পরিবর্তে যদি আমাদের সাধারণ ফাংশন থাকে তবে ডাকা ফাংশনে ম্যাপযুক্ত ফলাফলটি কীভাবে চিহ্নিত করব?
ট্রাগাকনাইটে

কোন বিষয় নয়, আমি একটি উপায় খুঁজে পেয়েছি - এর পরিবর্তে এই রান_সিপু_টাস্কস_ইন_প্যারালাল ([ল্যাম্বদা: মুদ্রণ ('সিপিইউ টাস্ক 1 চলছে!')), লাম্বদা: মুদ্রণ ('সিপিইউ টাস্ক 2 চলছে!'),]) এটি ব্যবহার করুন - ফলাফল = রান_ইও_টাস্কস_ইন_প্যারালাল ([ল্যাম্বদা: is 'ইস_সোমিংথিং ১': ফানক 1 ()}, ল্যাম্বদা: {'ইস_সোমথিং 2': ফানক 2 ()},])
ট্রাগাকনাইট

যদি ফাংশনটি বিভিন্ন পরামিতিগুলির আউটপুট দেয়, কীভাবে সেগুলি সংরক্ষণ করবেন। lambda: print('CPU task 1 running!'), lambda: print('CPU task 2 running!'),ফলটি পরিবর্তনশীলগুলিতে সংযোজন করার জায়গায় কী স্থাপন করা উচিত task1_outputএবংtask2_output
সায় কিরান

5

আপনি যদি উইন্ডোজ ব্যবহারকারী হন এবং পাইথন 3 ব্যবহার করেন, তবে এই পোস্টটি আপনাকে পাইথনে সমান্তরাল প্রোগ্রামিং করতে সহায়তা করবে hen আপনি যখন একটি সাধারণ মাল্টিপ্রসেসিং লাইব্রেরির পুল প্রোগ্রামিং পরিচালনা করেন, আপনি আপনার প্রোগ্রামের মূল ফাংশন সম্পর্কিত একটি ত্রুটি পাবেন। এটি কারণ উইন্ডোজের কোনও কাঁটাচামচ () কার্যকারিতা নেই। নীচের পোস্টটি উল্লিখিত সমস্যার সমাধান দিচ্ছে।

http://python.6.x6.nabble.com/ মাল্টিপ্রোসেসিং- পুল- wes-td5047050.html

যেহেতু আমি অজগর 3 ব্যবহার করছিলাম, তাই আমি প্রোগ্রামটি কিছুটা এভাবে পরিবর্তন করেছি:

from types import FunctionType
import marshal

def _applicable(*args, **kwargs):
  name = kwargs['__pw_name']
  code = marshal.loads(kwargs['__pw_code'])
  gbls = globals() #gbls = marshal.loads(kwargs['__pw_gbls'])
  defs = marshal.loads(kwargs['__pw_defs'])
  clsr = marshal.loads(kwargs['__pw_clsr'])
  fdct = marshal.loads(kwargs['__pw_fdct'])
  func = FunctionType(code, gbls, name, defs, clsr)
  func.fdct = fdct
  del kwargs['__pw_name']
  del kwargs['__pw_code']
  del kwargs['__pw_defs']
  del kwargs['__pw_clsr']
  del kwargs['__pw_fdct']
  return func(*args, **kwargs)

def make_applicable(f, *args, **kwargs):
  if not isinstance(f, FunctionType): raise ValueError('argument must be a function')
  kwargs['__pw_name'] = f.__name__  # edited
  kwargs['__pw_code'] = marshal.dumps(f.__code__)   # edited
  kwargs['__pw_defs'] = marshal.dumps(f.__defaults__)  # edited
  kwargs['__pw_clsr'] = marshal.dumps(f.__closure__)  # edited
  kwargs['__pw_fdct'] = marshal.dumps(f.__dict__)   # edited
  return _applicable, args, kwargs

def _mappable(x):
  x,name,code,defs,clsr,fdct = x
  code = marshal.loads(code)
  gbls = globals() #gbls = marshal.loads(gbls)
  defs = marshal.loads(defs)
  clsr = marshal.loads(clsr)
  fdct = marshal.loads(fdct)
  func = FunctionType(code, gbls, name, defs, clsr)
  func.fdct = fdct
  return func(x)

def make_mappable(f, iterable):
  if not isinstance(f, FunctionType): raise ValueError('argument must be a function')
  name = f.__name__    # edited
  code = marshal.dumps(f.__code__)   # edited
  defs = marshal.dumps(f.__defaults__)  # edited
  clsr = marshal.dumps(f.__closure__)  # edited
  fdct = marshal.dumps(f.__dict__)  # edited
  return _mappable, ((i,name,code,defs,clsr,fdct) for i in iterable)

এই ফাংশনটির পরে, উপরের সমস্যা কোডটিও এর পরিবর্তে কিছুটা পরিবর্তন করা হয়েছে:

from multiprocessing import Pool
from poolable import make_applicable, make_mappable

def cube(x):
  return x**3

if __name__ == "__main__":
  pool    = Pool(processes=2)
  results = [pool.apply_async(*make_applicable(cube,x)) for x in range(1,7)]
  print([result.get(timeout=10) for result in results])

এবং আমি ফলাফল হিসাবে পেয়েছি:

[1, 8, 27, 64, 125, 216]

আমি ভাবছি যে এই পোস্টটি কিছু উইন্ডো ব্যবহারকারীর জন্য দরকারী হতে পারে।


5

মনে হচ্ছে আপনার একটি ফাংশন রয়েছে যা আপনাকে দুটি পৃথক পরামিতি কল করতে হবে। এই এইরূপ সূচারূভাবে একটি সমন্বয় ব্যবহার করে কাজ করা যেতে পারে concurrent.futuresএবং mapপাইথন সঙ্গে 3.2+

import time
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor

def sleep_secs(seconds):
  time.sleep(seconds)
  print(f'{seconds} has been processed')

secs_list = [2,4, 6, 8, 10, 12]

এখন, যদি আপনার অপারেশনটি আইও আবদ্ধ হয়, তবে আপনি এটি ব্যবহার করতে পারেন ThreadPoolExecutor:

with ThreadPoolExecutor() as executor:
  results = executor.map(sleep_secs, secs_list)

আর্গুমেন্টের তালিকায় আপনার ফাংশনে mapএখানে কীভাবে ব্যবহৃত হয় তা নোট করুন map

এখন, যদি আপনার ফাংশনটি সিপিইউ সীমাবদ্ধ থাকে তবে আপনি ব্যবহার করতে পারেন ProcessPoolExecutor

with ProcessPoolExecutor() as executor:
  results = executor.map(sleep_secs, secs_list)

আপনি যদি নিশ্চিত না হন তবে আপনি উভয়ই চেষ্টা করে দেখতে পারেন এবং কোনটি আপনাকে আরও ভাল ফলাফল দেয়।

অবশেষে, আপনি যদি নিজের ফলাফলগুলি মুদ্রণ করতে চান তবে আপনি কেবল এটি করতে পারেন:

with ThreadPoolExecutor() as executor:
  results = executor.map(sleep_secs, secs_list)
  for result in results:
    print(result)

4

গ্যারান্টি দেওয়ার কোনও উপায় নেই যে দুটি ফাংশন একে অপরের সাথে সিঙ্কে কার্যকর হবে যা দেখে মনে হয় আপনি যা করতে চান।

আপনি যা করতে পারেন তা হ'ল ফাংশনটি কয়েকটি ধাপে বিভক্ত করা, তারপরে Process.join@ আইস এর উত্তর উল্লেখের মতো ব্যবহার করে সমালোচনামূলক সিঙ্ক্রোনাইজেশন পয়েন্টে উভয়ের সমাপ্তির জন্য অপেক্ষা করুন ।

এটির চেয়ে ভাল time.sleep(10)কারণ আপনি সঠিক সময়ের গ্যারান্টি দিতে পারবেন না। স্পষ্টভাবে অপেক্ষা করে আপনি বলছেন যে ফাংশনগুলি অবশ্যই পরবর্তী পদক্ষেপে যাওয়ার আগে সেই পদক্ষেপটি সম্পাদন করতে হবে, ধরে নেওয়ার পরিবর্তে এটি 10 ​​মিমি এর মধ্যে সম্পন্ন হবে যা মেশিনে আর কি চলছে তার ভিত্তিতে গ্যারান্টিযুক্ত নয়।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.