থ্রেড পুলের চেয়ে কাঁটাচামচ / যোগদানের কাঠামো কীভাবে ভাল?


134

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

উদাহরণস্বরূপ, টিউটোরিয়াল উদাহরণে সমান্তরাল ঝাপসা আলগোরিদমটি এভাবে প্রয়োগ করা যেতে পারে:

public class Blur implements Runnable {
    private int[] mSource;
    private int mStart;
    private int mLength;
    private int[] mDestination;

    private int mBlurWidth = 15; // Processing window size, should be odd.

    public ForkBlur(int[] src, int start, int length, int[] dst) {
        mSource = src;
        mStart = start;
        mLength = length;
        mDestination = dst;
    }

    public void run() {
        computeDirectly();
    }

    protected void computeDirectly() {
        // As in the example, omitted for brevity
    }
}

শুরুতে বিভক্ত করুন এবং থ্রেড পুলে কাজগুলি প্রেরণ করুন:

// source image pixels are in src
// destination image pixels are in dst
// threadPool is a (cached) thread pool

int maxSize = 100000; // analogous to F-J's "sThreshold"
List<Future> futures = new ArrayList<Future>();

// Send stuff to thread pool:
for (int i = 0; i < src.length; i+= maxSize) {
    int size = Math.min(maxSize, src.length - i);
    ForkBlur task = new ForkBlur(src, i, size, dst);
    Future f = threadPool.submit(task);
    futures.add(f);
}

// Wait for all sent tasks to complete:
for (Future future : futures) {
    future.get();
}

// Done!

কাজগুলি থ্রেড পুলের কাতারে যায়, সেখান থেকে শ্রমিকের থ্রেডগুলি উপলভ্য হওয়ার সাথে সাথে তারা কার্যকর করা হয়। যতক্ষণ না বিভাজন যথেষ্ট পরিমাণে দানাদার (বিশেষত শেষ কাজটির জন্য অপেক্ষা করা এড়াতে) এবং থ্রেড পুলটিতে যথেষ্ট পরিমাণে (প্রসেসরের কমপক্ষে এন) থ্রেড রয়েছে যতক্ষণ না সমস্ত প্রসেসর সম্পূর্ণ গতিতে কাজ করে যতক্ষণ না সম্পূর্ণ গণনা সম্পন্ন হয়।

আমি কিছু অনুপস্থিত করছি? কাঁটাচামচ / জয়েন ফ্রেমওয়ার্ক ব্যবহারের অতিরিক্ত মূল্য কী?

উত্তর:


136

আমি মনে করি যে মূল ভুল বোঝাবুঝিটি হ'ল, কাঁটাচামচ / যোগদানের উদাহরণগুলি কাজের চুরি দেখায় না তবে কেবলমাত্র এক ধরণের স্ট্যান্ডার্ড বিভাজন এবং বিজয় অর্জন করে।

কাজের চুরি এমন হবে: শ্রমিক বি তার কাজ শেষ করেছেন। তিনি একজন দয়ালু, তাই তিনি চারপাশে তাকিয়ে দেখেন ওয়ার্কার এ এখনও খুব পরিশ্রম করছে। সে উপচে পড়ে জিজ্ঞাসা করে: "ওহে ছেলে, আমি তোমাকে একটা হাত দিতে পারি।" একটি উত্তর। "শীতল, আমার 1000 টি ইউনিটের এই কাজটি রয়েছে So এখন পর্যন্ত আমি 655 রেখে 345 টি শেষ করেছি you বি বলেছেন "ঠিক আছে, আসুন শুরু করা যাক আমরা আগে পাব যেতে পারি।"

আপনি দেখুন - প্রকৃত কাজ শুরু করার পরেও শ্রমিকদের অবশ্যই একে অপরের মধ্যে যোগাযোগ করতে হবে। এটি উদাহরণগুলির অনুপস্থিত অংশ।

অন্যদিকে উদাহরণগুলি কেবল "সাবকন্ট্র্যাক্টর ব্যবহার করুন" এর মতো কিছু দেখায়:

কর্মী এ: "ডাং, আমার 1000 ইউনিট কাজ রয়েছে me আমার পক্ষে খুব বেশি I'll আমি নিজে 500 করব এবং অন্য কারও সাথে 500 টি সাবকন্ট্র্যাক্ট করব।" বড় কাজটি প্রতিটি 10 ​​টি ইউনিটের ছোট প্যাকেটে বিভক্ত না হওয়া অবধি এটি চলে। এগুলি উপলব্ধ কর্মীরা দ্বারা কার্যকর করা হবে। তবে যদি কোনও প্যাকেট এক প্রকারের বিষের বড়ি হয় এবং অন্যান্য প্যাকেটের তুলনায় যথেষ্ট সময় নেয় - ভাগ্য খারাপ, বিভাজনের পর্ব শেষ হয়ে গেছে।

কাঁটাচামচ / যোগদান এবং টাস্কটি সামনের দিকে বিভক্ত করার মধ্যে কেবলমাত্র বাকি পার্থক্যটি হ'ল: সামনে বিভাজন করার সময় আপনি শুরু থেকেই পুরো কাজটি সারিতে থাকবেন। উদাহরণ: 1000 ইউনিট, প্রান্তিক হ'ল 10, তাই সারিটিতে 100 টি প্রবেশ রয়েছে ries এই প্যাকেটগুলি থ্রেডপুল সদস্যদের বিতরণ করা হয়।

কাঁটাচামচ / যোগদান আরও জটিল এবং সারিতে প্যাকেটের সংখ্যা আরও কম রাখার চেষ্টা করে:

  • পদক্ষেপ 1: একটি প্যাকেট (1 ... 1000) কাতারে রাখুন
  • পদক্ষেপ 2: একজন শ্রমিক প্যাকেটটি পপ করে (1 ... 1000) এবং দুটি প্যাকেট দিয়ে এটি প্রতিস্থাপন করে: (1 ... 500) এবং (501 ... 1000)
  • পদক্ষেপ 3: এক শ্রমিক প্যাকেট পপ করে (500 ... 1000) এবং ধাক্কা দেয় (500 ... 750) এবং (751 ... 1000)।
  • পদক্ষেপ এন: স্ট্যাকটিতে এই প্যাকেটগুলি রয়েছে: (1..500), (500 ... 750), (750 ... 875) ... (991..1000)
  • পদক্ষেপ এন + 1: প্যাকেট (991..1000) পপড এবং কার্যকর করা হয়েছে
  • পদক্ষেপ এন + 2: প্যাকেট (981..990) পপড এবং কার্যকর করা হয়েছে
  • পদক্ষেপ এন + 3: প্যাকেট (961..980) পপড এবং বিভক্ত (961 ... 970) এবং (971..980)। ....

আপনি দেখুন: কাঁটাচামচায় / যোগদানের সারিটি ছোট (উদাহরণে)) এবং "বিভক্ত" এবং "কার্য" পর্যায়গুলি আন্তঃস্তৃত রয়েছে।

যখন একাধিক কর্মীরা পপিং এবং এক সাথে চাপ দিচ্ছেন তখন মিথস্ক্রিয়াগুলি অবশ্যই এতটা স্পষ্ট হয় না।


আমি মনে করি এটি সত্যই উত্তর। আমি অবাক হয়েছি যে এখানে যদি সত্যিকারের কাঁটাচামচ / যোগদানের উদাহরণ রয়েছে যা কোথাও এর কাজ চুরির ক্ষমতাও প্রদর্শন করে? প্রাথমিক উদাহরণগুলির সাথে কাজের চাপের পরিমাণ ইউনিটের আকার (উদাহরণস্বরূপ অ্যারে দৈর্ঘ্য) থেকে পুরোপুরি অনুমানযোগ্য, সুতরাং সামনে বিভক্তকরণ সহজ। চুরি করা সমস্যাগুলিতে অবশ্যই পার্থক্য তৈরি করতে পারে যেখানে ইউনিট প্রতি কাজের চাপের পরিমাণ ইউনিটের আকার থেকে ভাল অনুমানযোগ্য নয়
জুনাস পুলক্কা

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

@ মার্ক: আমি দুঃখিত, তবে এর কোন উদাহরণ আমার কাছে নেই।
এএইচ

6
ওরাকল এর উদাহরণ, আইএমওর সাথে সমস্যাটি এই নয় যে এটি কাজ চুরিটি প্রদর্শন করে না (এটি এএইচ দ্বারা বর্ণিত হয়েছে) তবে এটি একটি সহজ থ্রেডপুলের জন্য একটি অ্যালগরিদম কোড করা সহজ যা এটি (যেমন জুনাসে করেছে) did এফজে সর্বাধিক কার্যকর হয় যখন কাজটি পর্যাপ্ত স্বতন্ত্র কাজগুলিতে প্রাক-বিভক্ত হতে পারে না তবে পুনরাবৃত্তভাবে কাজগুলিতে বিভক্ত হতে পারে যা নিজেদের মধ্যে স্বতন্ত্র। উদাহরণ হিসাবে আমার উত্তর দেখুন
ashirley

2
কাজের চুরি যেখানে কার্যকর হতে পারে তার কয়েকটি উদাহরণ: h-online.com/developer/features/…
ভোলি

27

আপনার যদি এন ব্যস্ত থ্রেডগুলি সমস্তভাবে স্বাধীনভাবে 100% এ দূরে কাজ করে থাকে, তবে এটি একটি কাঁটাচামড়ার অংশ (এফজে) পুলের এন থ্রেডের চেয়ে ভাল হবে। তবে এটি কখনই সেভাবে কার্যকর হয় না।

সমস্যাটিকে ঠিক সমান অংশে ভাগ করতে পারে না। এমনকি যদি আপনি করেন, থ্রেড শিডিয়ুলিং ন্যায্য হওয়ার কিছুটা পথ। আপনি ধীরতম থ্রেডটির অপেক্ষায় শেষ করবেন। আপনার যদি একাধিক টাস্ক থাকে তবে তারা প্রতিটি এন-ওয়ে সমান্তরালতা (সাধারণত আরও দক্ষ) এর চেয়ে কম চালাতে পারে, তবুও অন্যান্য কাজ শেষ হয়ে গেলে এন-ওয়ে পর্যন্ত যেতে পারে।

তাহলে আমরা কেন এফজে-আকারের টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো করা উচিত না সাধারণ এফজে ব্যবহারের ফলে সমস্যাটিকে ছোট ছোট টুকরো টুকরো করা হয়। এলোমেলো ক্রমে এগুলি করতে একটি হার্ডওয়্যার স্তরে অনেক সমন্বয় প্রয়োজন। ওভারহেডগুলি হত্যাকারী হবে। এফজে-তে, টাস্কগুলি একটি সারিতে রাখা হয় যা থ্রেড লাস্ট ইন ফার্স্ট আউট অর্ডারে পড়ে (লিফো / স্ট্যাক), এবং কাজ স্টিলিং (মূল কাজ হিসাবে, সাধারণত) ফার্স্ট ইন আউট (ফিফো / "সারি") সম্পন্ন হয়। ফলস্বরূপ যে দীর্ঘ অ্যারে প্রসেসিংটি ক্ষুদ্র অংশগুলিতে বিভক্ত হয়েও মূলত ক্রমানুসারে করা যেতে পারে। (এটি এমন একটি ক্ষেত্রেও হতে পারে যে সমস্যাটিকে একটি বড় আকারের আকারে ছোট আকারের সমান আকারে ছড়িয়ে দেওয়া তুচ্ছ হতে পারে না Say

উপসংহার: এফজে একটি অসম পরিস্থিতিতে হার্ডওয়্যার থ্রেডগুলির আরও দক্ষ ব্যবহারের অনুমতি দেয়, আপনার যদি একাধিক থ্রেড থাকে তবে সর্বদা থাকবে।


তবে কেন এফজে খুব ধীরতম সুতোর জন্য অপেক্ষা করবে না? পূর্ব-নির্ধারিত সংখ্যক সাব-টাস্ক রয়েছে এবং অবশ্যই তাদের মধ্যে কিছু সর্বদা শেষ হবে। maxSizeআমার উদাহরণে প্যারামিটারটি সামঞ্জস্য করা এফজে উদাহরণে "বাইনারি বিভাজন" হিসাবে প্রায় অনুরূপ সাবটাস্ক বিভাগ তৈরি করে ( compute()পদ্ধতিটির মধ্যে সম্পন্ন হয়, যা কোনও কিছুর গণনা করে বা সাবটাস্ক প্রেরণ করে invokeAll())।
জুনাস পুলক্কা

কারণ এগুলি অনেক ছোট - আমি আমার উত্তরে যুক্ত করব।
টম হাটিন -

ঠিক আছে, যদি সাবটাস্কগুলির সংখ্যাটি সমান্তরালভাবে প্রকৃতভাবে প্রক্রিয়াকরণ করা যায় (যা বোঝায়, শেষটির জন্য অপেক্ষা করতে না পারে) এর চেয়ে বৃহত্তর (গুলি) এর ক্রম হয় তবে আমি সমন্বয়ের বিষয়গুলি দেখতে পাচ্ছি। বিভাগটি সেই দানাদার বলে মনে করা হলে এফজে উদাহরণটি বিভ্রান্তিকর হতে পারে: এটি 100000 এর একটি প্রান্তিক ব্যবহার করে, যা 1000x1000 চিত্রের জন্য 16 টি প্রকৃত সাবটাস্ক তৈরি করবে, প্রতিটি প্রক্রিয়াকরণ 62500 উপাদান। 10000x10000 চিত্রের জন্য এখানে 1024 সাবটাস্ক থাকবে যা ইতিমধ্যে কিছু।
জুনাস পুলক্কা

19

থ্রেড পুলের চূড়ান্ত লক্ষ্য এবং কাঁটাচামচ / যোগদান একই রকম: দু'জনই উপলব্ধ সিপিইউ শক্তিটিকে সর্বোচ্চ থ্রুপুটের জন্য সর্বোত্তমভাবে ব্যবহার করতে চায়। সর্বাধিক থ্রুটপুটটির অর্থ হ'ল সম্ভব দীর্ঘ সময়কালে যতগুলি কাজ সম্পন্ন করা উচিত। এটা করার দরকার কী? (নিম্নলিখিতগুলির জন্য আমরা ধরে নেব যে গণনার কার্যগুলির কোনও অভাব নেই: 100% সিপিইউ ব্যবহারের জন্য সবসময় যথেষ্ট পরিমাণে থাকে। অতিরিক্ত আমি হাইপার-থ্রেডিংয়ের ক্ষেত্রে কোর বা ভার্চুয়াল কোরগুলির জন্য সমানভাবে "সিপিইউ" ব্যবহার করি)।

  1. কমপক্ষে সেখানে অনেকগুলি থ্রেড চালানো দরকার যতটা সিপিইউ উপলব্ধ রয়েছে, কারণ কম থ্রেড চালানো একটি কোরকে অব্যবহৃত রাখবে।
  2. সর্বোচ্চ সিপিইউ রয়েছে তত বেশি থ্রেড চলমান থাকতে হবে, কারণ আরও থ্রেড চালানো তফসিলিকারীর জন্য অতিরিক্ত লোড তৈরি করবে যারা সিপিইউকে বিভিন্ন থ্রেডগুলিতে বরাদ্দ করে যা কিছু সিপিইউ সময়কে আমাদের কম্পিউটেশনাল টাস্কের পরিবর্তে শিডিয়ুলারে যেতে দেয়।

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

সুতরাং আপনি কখন থ্রেড পুল নিয়ে সমস্যায় পড়বেন? এটি যদি কোনও থ্রেড ব্লক করে , কারণ আপনার থ্রেডটি অন্য কোনও কাজ শেষ হওয়ার অপেক্ষায় রয়েছে। নিম্নলিখিত উদাহরণটি ধরুন:

class AbcAlgorithm implements Runnable {
    public void run() {
        Future<StepAResult> aFuture = threadPool.submit(new ATask());
        StepBResult bResult = stepB();
        StepAResult aResult = aFuture.get();
        stepC(aResult, bResult);
    }
}

আমরা এখানে যা দেখছি তা হল একটি অ্যালগরিদম যা এ, বি এবং সি তিনটি ধাপ নিয়ে গঠিত এবং ক এবং বি একে অপরের থেকে স্বতন্ত্রভাবে সম্পাদন করা যেতে পারে তবে ধাপ সি এর ধাপ A এবং B এর ফলাফলের দরকার যা এই অ্যালগরিদমটি কাজ টাস্ক এ জমা দেয় থ্রেডপুল এবং সরাসরি টাস্ক বি সম্পাদন। এর পরে থ্রেডটি টাস্ক-এ এর পাশাপাশি সম্পন্ন হওয়ার অপেক্ষায় থাকবে এবং সি ধাপে সিটি চালিয়ে যাবে, যদি ক এবং বি একই সময়ে সম্পন্ন হয়, তবে সবকিছু ঠিক আছে। তবে যদি A এর চেয়ে বেশি সময় লাগে? এটি এ কারণ হতে পারে কারণ টাস্ক এ এর ​​প্রকৃতি এটি নির্দেশ করে, তবে এটি এমনও হতে পারে কারণ টাস্কের জন্য থ্রেড না থাকায় শুরুতে উপলব্ধ একটি টাস্কটি অপেক্ষা করা দরকার। (যদি কেবলমাত্র একটি একক সিপিইউ উপলব্ধ থাকে এবং এইভাবে আপনার থ্রেডপুলটিতে একটি মাত্র থ্রেড থাকে এটি এমনকি অচলাবস্থার কারণ হয়ে দাঁড়ায়, তবে আপাতত এটি বিন্দু ছাড়াও রয়েছে)। মুল বক্তব্যটি হ'ল যে থ্রেডটি সবেমাত্র টাস্ক বি কার্যকর করেছেব্লক পুরো থ্রেড । যেহেতু আমাদের একই সংখ্যক থ্রেড সিপিইউ হিসাবে রয়েছে এবং একটি থ্রেড ব্লক করা হয়েছে যার অর্থ একটি সিপিইউ নিষ্ক্রিয়

কাঁটাচামচ / যোগদান এই সমস্যার সমাধান করে: কাঁটাচামচ / জোড় ফ্রেমওয়ার্কে আপনি নীচে একই অ্যালগরিদম লিখতে চাই:

class AbcAlgorithm implements Runnable {
    public void run() {
        ATask aTask = new ATask());
        aTask.fork();
        StepBResult bResult = stepB();
        StepAResult aResult = aTask.join();
        stepC(aResult, bResult);
    }
}

দেখতে একই রকম, তাই না? তবে খেই যে aTask.join ব্লক করা হবে না । পরিবর্তে এখানেই যেখানে কাজ-চুরি কার্যকর হয়: থ্রেডটি অন্যান্য কাজগুলির সন্ধান করবে যা অতীতে তৈরি হয়েছিল এবং সেগুলি অবিরত থাকবে। প্রথমে এটি যাচাই করে যে কাজগুলি নিজেই তৈরি করেছে তা প্রক্রিয়াজাতকরণ শুরু করেছে কিনা তা যাচাই করে। সুতরাং যদি এটিকে অন্য থ্রেড দ্বারা এখনও না শুরু করা হয় তবে এটি একটি পরবর্তী কাজ করবে, অন্যথায় এটি অন্যান্য থ্রেডের সারিটি পরীক্ষা করবে এবং তাদের কাজ চুরি করবে। অন্য থ্রেডের এই অন্য কাজটি শেষ হয়ে গেলে এটি এখনই সম্পন্ন হয়েছে কিনা তা পরীক্ষা করে দেখবে। এটি যদি উপরের অ্যালগরিদম কল করতে পারে stepC। অন্যথায় এটি চুরি করার জন্য আরও একটি কাজ সন্ধান করবে। সুতরাং কাঁটাচামচ / যোগদানের পুলগুলি 100% সিপিইউ ব্যবহার করতে পারে, এমনকি ব্লকিং ক্রিয়াকলাপের পরেও

তবে একটি ফাঁদ আছে: কাজের চুরি কেবলমাত্র এস এর joinকলের জন্য সম্ভব ForkJoinTask। এটি অন্য থ্রেডের জন্য অপেক্ষা করা বা আই / ও ক্রমের জন্য অপেক্ষা করার মতো বাহ্যিক অবরুদ্ধ ক্রিয়াকলাপগুলির জন্য করা যায় না। তাহলে কী, আই / ও শেষ হওয়ার জন্য অপেক্ষা করা একটি সাধারণ কাজ? এই ক্ষেত্রে যদি আমরা কাঁটাচামচ / যোগদানের পুলে একটি অতিরিক্ত থ্রেড যুক্ত করতে পারি যা ব্লকিংয়ের ক্রিয়াটি শেষ হওয়ার সাথে সাথে আবার বন্ধ হয়ে যাবে তবে এটি দ্বিতীয় সেরা কাজ হবে। এবং ForkJoinPoolক্যান আসলে এটি করতে পারে যদি আমরা ManagedBlockerগুলি ব্যবহার করি ।

ফিবানচি

ইন RecursiveTask জন্য JavaDoc ব্যবহার ফর্ক / যোগদান ফিবানচি সংখ্যার হিসাব করার একটি উদাহরণ। একটি ক্লাসিক পুনরাবৃত্ত সমাধানের জন্য দেখুন:

public static int fib(int n) {
    if (n <= 1) {
        return n;
    }
    return fib(n - 1) + fib(n - 2);
}

যেমন জাভাডোকস হিসাবে ব্যাখ্যা করা হয়েছে এটি ফাইবোনাচি সংখ্যা গণনা করার জন্য এটি একটি সুন্দর ডাম্প উপায়, কারণ এই অ্যালগরিদমে ও (2 ^ n) জটিলতা রয়েছে যখন সহজ উপায়গুলি সম্ভব। তবে এই অ্যালগরিদমটি খুব সহজ এবং সহজে বোঝা যায়, তাই আমরা এটির সাথে আছি। ধরে নেওয়া যাক আমরা কাঁটাচামচ / যোগদানের মাধ্যমে এটির গতি বাড়িয়ে তুলতে চাই। একটি নিষ্পাপ বাস্তবায়ন এইরকম দেখাবে:

class Fibonacci extends RecursiveTask<Long> {
    private final long n;

    Fibonacci(long n) {
        this.n = n;
    }

    public Long compute() {
        if (n <= 1) {
            return n;
        }
        Fibonacci f1 = new Fibonacci(n - 1);
        f1.fork();
        Fibonacci f2 = new Fibonacci(n - 2);
        return f2.compute() + f1.join();
   }
}

এই টাস্কটি যে ধাপগুলিতে বিভক্ত হয়েছে সেগুলি খুব ছোট এবং এইভাবে এটি ভয়াবহভাবে সঞ্চালন করবে তবে আপনি দেখতে পাচ্ছেন যে কাঠামোটি সাধারণত কীভাবে খুব ভালভাবে কাজ করে: দুটি সমানকে স্বাধীনভাবে গণনা করা যায়, তবে তারপরে ফাইনালটি তৈরি করার জন্য আমাদের উভয়ের প্রয়োজন need ফলাফল. সুতরাং অর্ধেকটি অন্য থ্রেডে করা হয়। অচলাবস্থা না পেয়ে থ্রেড পুলগুলির সাথে একই কাজ করতে মজা করুন (সম্ভব, তবে প্রায় সহজ নয়)।

কেবলমাত্র সম্পূর্ণতার জন্য: আপনি যদি এই পুনরাবৃত্ত পদ্ধতির ব্যবহার করে ফিবোনাচি নম্বরগুলি প্রকৃতপক্ষে গণনা করতে চান তবে এটি একটি অনুকূলিত সংস্করণ:

class FibonacciBigSubtasks extends RecursiveTask<Long> {
    private final long n;

    FibonacciBigSubtasks(long n) {
        this.n = n;
    }

    public Long compute() {
        return fib(n);
    }

    private long fib(long n) {
        if (n <= 1) {
            return 1;
        }
        if (n > 10 && getSurplusQueuedTaskCount() < 2) {
            final FibonacciBigSubtasks f1 = new FibonacciBigSubtasks(n - 1);
            final FibonacciBigSubtasks f2 = new FibonacciBigSubtasks(n - 2);
            f1.fork();
            return f2.compute() + f1.join();
        } else {
            return fib(n - 1) + fib(n - 2);
        }
    }
}

এটি সাবটাস্কগুলিকে অনেক ছোট রাখে কারণ এগুলি কেবল যখন n > 10 && getSurplusQueuedTaskCount() < 2সত্য হয় তখনই বিভক্ত হয়, যার অর্থ এখানে করার জন্য উল্লেখযোগ্যভাবে 100 টিরও বেশি পদ্ধতি কল রয়েছে ( n > 10) এবং ইতিমধ্যে অপেক্ষা করা খুব বেশি লোকের কাজ নেই ( getSurplusQueuedTaskCount() < 2)।

আমার কম্পিউটারে (4 টি (8 হাইপার-থ্রেডিং গণনা করার সময়), ইন্টেল (আর) কোর (টিএম) i7-2720QM সিপিইউ @ 2.20GHz) fib(50)ক্লাসিক পদ্ধতির সাথে seconds৪ সেকেন্ড এবং কাঁটাচামচ / যোগদানের পদ্ধতির সাথে মাত্র 18 সেকেন্ড সময় নেয় তাত্ত্বিকভাবে যতটা সম্ভব সম্ভব না হলেও এটি বেশ লক্ষণীয় লাভ।

সারসংক্ষেপ

  • হ্যাঁ, আপনার উদাহরণে কাঁটাচামচ / যোগদানের ক্লাসিক থ্রেড পুলগুলির তুলনায় কোনও সুবিধা নেই।
  • ব্লক জড়িত থাকার সময় কাঁটাচামচ / যোগদানের ক্রমশ কর্মক্ষমতা উন্নত করতে পারে
  • কাঁটাচামচ / যোগদান কিছু অচলাবস্থার সমস্যাগুলির সমাধান করে

17

কাঁটাচামচ / যোগদান কোনও থ্রেড পুলের থেকে আলাদা কারণ এটি কাজের চুরিটি প্রয়োগ করে। থেকে ফর্ক / যোগদান

যে কোনও এক্সিকিউটারসেবার মতো, কাঁটাচামচ / যোগদানের কাঠামোটি থ্রেড পুলে কর্মীদের থ্রেডগুলিতে বিতরণ করে। কাঁটাচামচ / যোগদানের কাঠামোটি পৃথক কারণ এটি একটি ওয়ার্ক-স্টিলিং অ্যালগরিদম ব্যবহার করে। কর্মী থ্রেডগুলি যা কাজ শেষ হয়ে যায় তা এখনও ব্যস্ত থাকা অন্যান্য থ্রেড থেকে কাজগুলি চুরি করতে পারে।

বলুন আপনার দুটি থ্রেড এবং 4 টি কাজ রয়েছে, বি, সি, ডি যা যথাক্রমে 1, 1, 5 এবং 6 সেকেন্ড নেয়। প্রাথমিকভাবে, a এবং b থ্রেড 1 এবং c এবং d কে 2 থ্রেডে বরাদ্দ করা হয়েছে একটি থ্রেড পুলে, এটি 11 সেকেন্ড সময় নিতে পারে। কাঁটাচামচ / যোগদানের সাথে থ্রেড 1 সমাপ্ত হবে এবং থ্রেড 2 থেকে কাজ চুরি করতে পারে, সুতরাং টাস্ক d থ্রেড দ্বারা সম্পাদিত হবে 1. থ্রেড 1 আ, খ এবং ডি, থ্রেড 2 কেবল গ। সামগ্রিক সময়: 8 সেকেন্ড, 11 নয়।

সম্পাদনা: জুনাস যেমন উল্লেখ করেছেন, কাজগুলি অগত্যা কোনও থ্রেডে প্রাক বরাদ্দ করা হয় না। কাঁটাচামচ / যোগদানের ধারণাটি একটি থ্রেড কোনও কাজকে একাধিক সাব-পিসে বিভক্ত করতে বেছে নিতে পারে। সুতরাং উপরের পুনরায় সেট করতে:

আমাদের দুটি কাজ (অ্যাবি) এবং (সিডি) রয়েছে যা যথাক্রমে 2 এবং 11 সেকেন্ড নেয়। থ্রেড 1 অ্যাব চালানো শুরু করে এবং এটিকে দুটি উপ-টাস্কে বিভক্ত করে। একইভাবে থ্রেড 2 এর সাথে এটি দুটি উপ-কার্য সি & ডিতে বিভক্ত হয়। থ্রেড 1 যখন একটি & বি শেষ করেছে, তখন এটি থ্রেড 2 থেকে ডি চুরি করতে পারে।


5
থ্রেড পুলগুলি সাধারণত থ্রেডপুলএক্সেকিউটারের উদাহরণ। এর মধ্যে, কার্যগুলি একটি কাতারে যায় ( অনুশীলনে ব্লকিংকুইউ ), যা থেকে শ্রমিক থ্রেডগুলি পূর্ববর্তী কাজ শেষ করার সাথে সাথেই কাজগুলি গ্রহণ করে। আমি যতদূর বুঝতে পারি নির্দিষ্ট থ্রেডগুলিতে কার্যগুলি পূর্বনির্ধারিত নয় । প্রতিটি থ্রেডে একযোগে (সর্বাধিক) ১ টি কাজ থাকে।
জুনাস পুলক্কা

4
আমি যতদূর জানি আছে এক জন্য সারি এক ThreadPoolExecutor যেটা ঘুরে নিয়ন্ত্রণ বিভিন্ন টপিক। এর অর্থ একটি নির্বাহকের কাছে টাস্ক বা রান্নেবলস (থ্রেডস নয়!) বরাদ্দকরণের কাজগুলিও নির্দিষ্ট থ্রেডগুলিতে পূর্বনির্ধারিত নয়। একেবারে উপায় উপায় এফজে এটিও করে। এখনও এফজে ব্যবহারের কোনও সুবিধা নেই।
হিঃ

1
@ হ্যাঁ, তবে কাঁটাচামচ / যোগদান আপনাকে বর্তমান কাজটি বিভক্ত করতে দেয়। থ্রেড যা কার্য সম্পাদন করছে তা এটিকে দুটি পৃথক কার্যে বিভক্ত করতে পারে। সুতরাং থ্রেডপুলএক্সেকিউটারের সাথে আপনার কার্যগুলির একটি নির্দিষ্ট তালিকা রয়েছে। কাঁটাচামচ / যোগদানের সাথে, এক্সিকিউটিভ টাস্কটি তার নিজস্ব কাজটিকে দুটি করে বিভক্ত করতে পারে, যা কাজ শেষ করার পরে অন্য থ্রেডগুলি ধরে নিতে পারে। অথবা আপনি যদি প্রথম শেষ করেন।
ম্যাথু ফারওয়েল

1
@ ম্যাথেজ ফারওয়েল : এফজে উদাহরণে , প্রতিটি টাস্কের মধ্যে, compute()হয় টাস্কটি গণনা করে, বা দুটি সাবটাস্কে বিভক্ত করে তোলে। কোন বিকল্পটি এটি চয়ন করে তা কেবলমাত্র টাস্কের আকারের উপর নির্ভর করে ( if (mLength < sThreshold)...তাই) এটি নির্দিষ্ট সংখ্যক কার্য তৈরির একটি অভিনব উপায়। 1000x1000 চিত্রের জন্য, ঠিক 16 টি সাবটাস্ক থাকবে যা আসলে কিছু গণনা করবে comp অতিরিক্তভাবে 15 (= 16 - 1) "অন্তর্বর্তী" কাজগুলি থাকবে যা কেবলমাত্র সাবটাস্কগুলি উত্পন্ন করে এবং অনুরোধ করে এবং নিজেই কিছু গণনা করে না।
জুনাস পুলক্কা

2
@ ম্যাথেজ ফারওয়েল: এটা সম্ভব যে আমি এফজে-র সমস্ত কিছুই বুঝতে পারি না, তবে কোনও সাবটাস্ক যদি তার computeDirectly()পদ্ধতিটি কার্যকর করার সিদ্ধান্ত নিয়েছে , তবে আর কিছুই চুরি করার উপায় নেই। পুরো বিভাজকটি একটি প্রাথমিক কাজ করা হয় , কমপক্ষে উদাহরণে।
জুনাস পুুলক্কা

14

উপরের প্রত্যেকে সঠিকভাবে কাজের চুরি দ্বারা প্রাপ্ত সুবিধাগুলি সঠিক, তবে এটি কেন তা প্রসারিত করার জন্য।

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

এর ফলাফল:

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

থ্রেড পুল ব্যবহার করে অন্যান্য বিভাজন এবং বিজয়ী প্রকল্পগুলির জন্য আরও আন্ত-থ্রেড যোগাযোগ এবং সমন্বয় প্রয়োজন।


13

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

বিষয় সম্পর্কে একটি সুন্দর নিবন্ধ এখানে । উদ্ধৃতি:

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


8

আর একটি গুরুত্বপূর্ণ পার্থক্য মনে হয় যে এফজে-র সাথে আপনি একাধিক, জটিল "যোগদান" পর্যায় করতে পারেন। Http://factory.ycp.edu/~dhovemey/spring2011/cs365/lecture/lecture18.html থেকে মার্জ সাজানোর বিষয়টি বিবেচনা করুন , এই কাজের প্রাক-বিভক্ত করার জন্য খুব বেশি অর্কিস্ট্রেশন প্রয়োজন। যেমন আপনার নিম্নলিখিত জিনিসগুলি করা প্রয়োজন:

  • প্রথম প্রান্তিকে সাজান
  • দ্বিতীয় প্রান্তিকে বাছাই করুন
  • প্রথম 2 চতুর্থাংশ একীভূত করুন
  • তৃতীয় ত্রৈমাসিক বাছাই করুন
  • সামনের চতুর্থাংশ সাজান
  • শেষ 2 চতুর্থাংশ একীভূত করুন
  • 2 অর্ধেক একত্রিত করুন

আপনি কীভাবে সুনির্দিষ্ট করেন যে মার্জগুলির আগে তাদের অবশ্যই পরিচালনা করতে হবে যা এগুলি সম্পর্কিত etc.

আমি প্রতিটি আইটেমের তালিকার প্রতিটিটির জন্য কীভাবে সেরা কিছু করতে পারি তা ভাল করে দেখছি। আমি মনে করি আমি কেবল তালিকাটি প্রাক-বিভক্ত করব এবং একটি মানক থ্রেডপুল ব্যবহার করব। এফজে সবচেয়ে কার্যকর বলে মনে হয় যখন কাজটি পর্যাপ্ত স্বতন্ত্র কাজগুলিতে প্রাক-বিভক্ত না হয়ে পুনরাবৃত্তভাবে এমন কাজগুলিতে বিভক্ত হতে পারে যা নিজেদের মধ্যে স্বতন্ত্র থাকে (যেমন, অর্ধগুলি বাছাই করা স্বতন্ত্র তবে 2 বাছাই করা অর্ধকে সাজানো পুরো অংশে মার্জ করা নয়)।


6

আপনার যখন ব্যয়বহুল মার্জ অপারেশন হয় তখন এফ / জে এরও একটি স্বতন্ত্র সুবিধা থাকে। যেহেতু এটি একটি গাছের কাঠামোর মধ্যে বিভক্ত হয় আপনি কেবল লগ 2 (এন) লিনিয়ার থ্রেড বিভাজনের সাথে এন মার্জগুলির বিপরীতে মার্জ করেন। (এটি তাত্ত্বিক অনুমান করে তোলে যে আপনার কাছে থ্রেড হিসাবে যতগুলি প্রসেসর রয়েছে তবে এখনও একটি সুবিধা) একটি হোম ওয়ার্ক অ্যাসাইনমেন্টের জন্য আমাদের প্রতিটি সূচকে মানগুলি সংযুক্ত করে কয়েক হাজার 2 ডি অ্যারে (সমস্ত একই মাত্রা) মার্জ করতে হয়েছিল। কাঁটাচামচ যোগদান এবং পি প্রসেসরের সাহায্যে পি 2 অনন্তের কাছে যাওয়ার সাথে সাথে লগ 2 (এন) এ পৌঁছায়।

1 2 3 .. 7 3 1 .... 8 5 4
4 5 6 + 2 4 3 => 6 9 9
7 8 9 .. 1 1 0 .... 8 9 9


3

ক্রলারের মতো অ্যাপ্লিকেশনটিতে আপনি ফোর্কজয়িনের অভিনয় দেখে অবাক হয়ে যাবেন। এখানে আপনি শিখতে হবে সেরা টিউটোরিয়াল

কাঁটাচামচ / যোগদানের যুক্তি খুব সহজ: (1) পৃথক (কাঁটাচামচ) প্রতিটি বৃহত কাজকে ছোট ছোট কাজের মধ্যে; (২) প্রতিটি কাজ পৃথক থ্রেডে প্রক্রিয়াকরণ করুন (প্রয়োজনে এগুলি আরও ছোট কার্যক্রমে পৃথক করে); (3) ফলাফল যোগদান।


3

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

সূত্র: http://www.oracle.com/technetwork/articles/java/fork-join-422606.html

বিভাজন এবং বিজয়ী অ্যালগরিদম বাস্তবায়নের জন্য এক্সিকিউটরদের সমস্যাটি সাবটাস্ক তৈরির সাথে সম্পর্কিত নয়, কারণ একজন কলযোগ্য তার এক্সিকিউটারের কাছে নতুন সাবটাস্ক জমা দিতে এবং তার ফলাফলটির জন্য সিঙ্ক্রোনাস বা অ্যাসিনক্রোনাস ফ্যাশনে অপেক্ষা করতে মুক্ত। বিষয়টি সমান্তরালতার of

ডাগ এলইএর প্রচেষ্টার মাধ্যমে জাভা এসই 7 এর জাভা.ইটিল.কন্ট্রেন্ট প্যাকেজে যুক্ত কাঁটাচামচ / যোগদানের কাঠামোটি সেই শূন্যস্থান পূরণ করে

সূত্র: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ForkJoinPool.html

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

পাবলিক ইন্ট getPoolSize () কর্মী থ্রেডগুলির সংখ্যা ফেরত দেয় যা শুরু হয়েছে তবে এখনও অবসান হয়নি। এই পদ্ধতির দ্বারা প্রাপ্ত ফলাফল getPa Parallism () থেকে আলাদা হতে পারে যখন অন্যেরা সহযোগিতামূলকভাবে অবরুদ্ধ থাকে তখন সমান্তরালতা বজায় রাখার জন্য থ্রেড তৈরি করা হয়।


2

যাদের দীর্ঘ উত্তর পড়ার মতো সময় নেই তাদের জন্য আমি একটি সংক্ষিপ্ত উত্তর যুক্ত করতে চাই। তুলনাটি অ্যাপ্লাইড আক্কা প্যাটার্নস বই থেকে নেওয়া হয়েছে:

কাঁটাচামচ-যুক্ত-এক্সিকিউটর বা থ্রেড-পুল-নির্বাহক ব্যবহার করবেন কিনা সে বিষয়ে আপনার সিদ্ধান্তটি মূলত সেই প্রেরণকারীর ক্রিয়াকলাপ অবরুদ্ধ করবে কিনা তার উপর ভিত্তি করে। একটি কাঁটাচামচ-যোগসূচক আপনাকে সর্বাধিক সংখ্যক সক্রিয় থ্রেড দেয়, যেখানে একটি থ্রেড-পুল-নির্বাহক আপনাকে একটি নির্দিষ্ট সংখ্যক থ্রেড দেয়। যদি থ্রেডগুলি অবরুদ্ধ করা হয়, তবে একটি কাঁটাচামচ-এক্সিকিউটার আরও তৈরি করতে পারে, যেখানে থ্রেড-পুল-এক্সিকিউটিউটর তৈরি করবে না। ব্লকিং অপারেশনের জন্য, আপনি সাধারণত থ্রেড-পুল-নির্বাহকের সাহায্যে আরও ভাল হন কারণ এটি আপনার থ্রেডের সংখ্যাগুলি বিস্ফোরিত হতে বাধা দেয়। কাঁটা-যোগ-নির্বাহকের পক্ষে আরও "প্রতিক্রিয়াশীল" অপারেশনগুলি আরও ভাল।

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