এক্সিকিউটর সার্ভিস, সমস্ত কাজ শেষ হওয়ার জন্য কীভাবে অপেক্ষা করবেন


196

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

ExecutorService es = Executors.newFixedThreadPool(2);
for (DataTable singleTable : uniquePhrases) {   
    es.execute(new ComputeDTask(singleTable));
}
try{
    es.wait();
} 
catch (InterruptedException e){
    e.printStackTrace();
}

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

uniquePhrasesকয়েক হাজার উপাদানকে ধারণ করে। আমার অন্য পদ্ধতি ব্যবহার করা উচিত? আমি যতটা সম্ভব সহজ কিছু সন্ধান করছি


1
আপনি যদি অপেক্ষাটি ব্যবহার করতে চান (উত্তরগুলি আপনাকে বলে না যে আপনি): আপনাকে সর্বদা অবজেক্টের সাথে সিঙ্ক্রোনাইজ করা প্রয়োজন (এক্ষেত্রে es) আপনি যখন অপেক্ষা করতে চান - লকটি স্বয়ংক্রিয়ভাবে অপেক্ষা করা অবস্থায় প্রকাশিত হবে
মিহি

13
ThreadPool আরম্ভ করতে একটি ভাল উপায় নেইExecutors.newFixedThreadPool(System.getRuntime().availableProcessors());

7
Runtime.getRuntime () availableProcessors ()।
ভিক গ্যামভ

[এই] [1] একটি আকর্ষণীয় বিকল্প নেই .. [1]: stackoverflow.com/questions/1250643/...
Răzvan Petruescu

1
আপনি যদি কাউন্টডাউনল্যাচটি ব্যবহার করতে চান তবে এটি উদাহরণ কোড: stackoverflow.com/a/44127101/4069305
Tuan Pham

উত্তর:


213

সবচেয়ে সহজ পন্থাটি হ'ল ব্যবহার করা ExecutorService.invokeAll()যা কোন ওয়ানলাইনারে আপনি যা চান তা করে। আপনার পার্লেন্সে ComputeDTaskআপনাকে প্রয়োগ করার জন্য সংশোধন করতে হবে বা মোড়ানো দরকার Callable<>যা আপনাকে আরও কিছুটা নমনীয়তা দিতে পারে। সম্ভবত আপনার অ্যাপ্লিকেশনটিতে একটি অর্থবহ বাস্তবায়ন রয়েছে Callable.call(), তবে এটি ব্যবহার না করা থাকলে এটি মোড়ানোর একটি উপায় এখানে Executors.callable()

ExecutorService es = Executors.newFixedThreadPool(2);
List<Callable<Object>> todo = new ArrayList<Callable<Object>>(singleTable.size());

for (DataTable singleTable: uniquePhrases) { 
    todo.add(Executors.callable(new ComputeDTask(singleTable))); 
}

List<Future<Object>> answers = es.invokeAll(todo);

অন্যরা যেমন উল্লেখ করেছে, আপনি invokeAll()উপযুক্ত হলে সময়সীমার সংস্করণটি ব্যবহার করতে পারেন । এই উদাহরণে, answersএকটি গোছা রয়েছে Futureযা নাল ফিরে আসবে (এর সংজ্ঞা দেখুন b Executors.callable()সম্ভবত আপনি যা করতে চান তা সামান্য রিফ্যাক্টরিং যাতে আপনি একটি দরকারী উত্তর ফিরে পেতে পারেন বা অন্তর্নিহিত একটি রেফারেন্স পেতে ComputeDTaskপারেন তবে আমি করতে পারি আপনার উদাহরণ থেকে বলতে হবে না।

এটি পরিষ্কার না হলে নোটটি invokeAll()সমস্ত কাজ শেষ না হওয়া পর্যন্ত ফিরে আসবে না। (যেমন, Futureআপনার answersসংগ্রহে থাকা সমস্ত গুলি .isDone()যদি জিজ্ঞাসা করা হয় তবে তারা জানিয়ে দেবে)) এটি সমস্ত ম্যানুয়াল শাটডাউন, ওয়েটটাইমিনেশন ইত্যাদিকে এড়িয়ে চলে ... এবং ExecutorServiceযদি ইচ্ছা হয় তবে আপনাকে এটি একাধিক চক্রের জন্য ঝরঝরে পুনরায় ব্যবহার করতে দেয় ।

এসও-তে কয়েকটি সম্পর্কিত প্রশ্ন রয়েছে:

এগুলির কোনওটিই আপনার প্রশ্নের পক্ষে কঠোরভাবে অন-পয়েন্ট নয়, তবে লোকেদের কীভাবে ব্যবহার করা উচিত বলে মনে হয় Executor/ ExecutorServiceসেগুলি সম্পর্কে তারা কিছুটা রঙ দেয় ।


9
আপনি যদি একটি ব্যাচে আপনার সমস্ত কাজ যুক্ত করে থাকেন এবং আপনি কলযোগ্যদের তালিকার সাথে তালিকায় থাকেন তবে এটি সঠিক but তবে আপনি যদি কলব্যাক বা ইভেন্ট-লুপ পরিস্থিতিতে এক্সিকিউটর সার্ভিস.সুবমিট () কে কল করেন তবে এটি কাজ করবে না।
Desty

2
আমার মনে হয় এটি এক্সিকিউটর সার্ভিস এর আর প্রয়োজন না হলে শটডাউন () এখনও কল করা উচিত, অন্যথায় থ্রেডগুলি কখনই শেষ হবে না (কোরপুলসাইজ = 0 বা অনুমতিকোরিথ্রেডটাইমআউট = সত্য হলে কেস বাদে) threads
29

আশ্চর্যজনক! শুধু আমি যা খুঁজছিলাম। উত্তর ভাগ করে নেওয়ার জন্য অনেক ধন্যবাদ। আমাকে এটি চেষ্টা করে দেখুন।
মোহাম্মদসানাউল্লা

59

আপনি যদি সমস্ত কাজ শেষ হওয়ার জন্য অপেক্ষা করতে চান তবে এর shutdownপরিবর্তে পদ্ধতিটি ব্যবহার করুন wait। তারপরে এটি অনুসরণ করুন awaitTermination

এছাড়াও, আপনি Runtime.availableProcessorsহার্ডওয়্যার থ্রেডগুলির সংখ্যা পেতে ব্যবহার করতে পারেন যাতে আপনি নিজের থ্রেডপুলটি সঠিকভাবে শুরু করতে পারেন।


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

1
@ আলাইন - ধন্যবাদ আমার অপেক্ষা করা উচিত ছিল। সংশোধন করা হয়েছে।
এনজি।

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

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

1
আপনি ঠিক বলেছেন, তবে এই উত্তরটি দেখুন - stackoverflow.com/a/1250655/263895 - আপনি সর্বদা এটি একটি অবিশ্বাস্যভাবে দীর্ঘ সময়সীমা দিতে পারেন
এনজি।

48

যদি সমস্ত কাজ ExecutorServiceশেষ করার জন্য অপেক্ষা করা অবিকল আপনার লক্ষ্য না হয়, তবে কোনও নির্দিষ্ট ব্যাচের কাজ শেষ না হওয়া পর্যন্ত অপেক্ষা করা হয় তবে আপনি একটি CompletionService- বিশেষত, একটি ব্যবহার করতে পারেন ExecutorCompletionService

ধারণাটি হ'ল ExecutorCompletionServiceআপনার একটি মোড়ক তৈরি করা Executor, এর মাধ্যমে কয়েকটি পরিচিত সংখ্যা জমা দিন এবং তারপরে (কোনটি ব্লক) বা (যা না) ব্যবহার করে সমাপ্তি সারিতে একই সংখ্যাটি অঙ্কন করুন । একবার আপনি জমা দেওয়া কাজের সাথে সম্পর্কিত সমস্ত প্রত্যাশিত ফলাফল টানলে, আপনি জানেন যে সেগুলি সব শেষ হয়ে গেছে।CompletionServicetake()poll()

আমাকে আরও একবার এই বক্তব্য রাখি, কারণ এটি ইন্টারফেস থেকে সুস্পষ্ট নয়: কতগুলি জিনিস CompletionServiceআঁকতে চেষ্টা করতে হবে তা জানতে আপনাকে অবশ্যই কতগুলি জিনিস রেখেছিলেন know এটি বিশেষত take()পদ্ধতিটির সাথে সম্পর্কিত: এটি একবারে অনেক বেশি কল করুন এবং এটির কোনও কল্পনা থ্রেডকে অবরুদ্ধ করে দেবে যতক্ষণ না অন্য থ্রেড একই সাথে অন্য কাজ জমা দেয় CompletionService

অনুশীলনে জাভা কনকুরન્સી বইটিতে কীভাবে ব্যবহার করতেCompletionService হয় তার কয়েকটি উদাহরণ রয়েছে


এটি আমার উত্তরের একটি ভাল পাল্টা পয়েন্ট - আমি বলবো প্রশ্নের সোজা উত্তর হ'ল ইনভোকএল (); তবে @ এসই ঠিক আছে যখন ইএসগুলিতে চাকরির গোষ্ঠীগুলি জমা দেওয়ার এবং তাদের শেষ হওয়ার অপেক্ষায় ...
জেএ

@ ওম-নম-নোম, লিঙ্কগুলি আপডেট করার জন্য আপনাকে ধন্যবাদ। উত্তরটি এখনও কার্যকর বলে আমি আনন্দিত।
Seh

1
ভাল উত্তর, আমি সম্পর্কে অবগত ছিল নাCompletionService
ভিক

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

11

আপনি নির্বাহ শেষ করতে নির্বাহক সেবা জন্য অপেক্ষা করতে চান, কল shutdown()এবং তারপর, awaitTermination (ইউনিট, unitType) যেমন awaitTermination(1, MINUTE)। এক্সিকিউটর সার্ভিস এর নিজস্ব মনিটরে বাধা দেয় না, তাই আপনি waitইত্যাদি ব্যবহার করতে পারবেন না etc.


আমি মনে করি এটি অপেক্ষা।
এনজি।

@ এসবি - ধন্যবাদ - আমি দেখছি আমার স্মৃতিশক্তি হ্রাসযোগ্য! আমি নামটি আপডেট করেছি এবং নিশ্চিত হতে একটি লিঙ্ক যুক্ত করেছি।
এমডিএমএ

অপেক্ষা করুন করার জন্য "সব সময় প্রবেশ করুন," ওটার মত ব্যবহার awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); stackoverflow.com/a/1250655/32453
rogerdpack

আমি মনে করি এটি সবচেয়ে সহজ পদ্ধতির
শেরভিন আসগরী

1
@ মোশিএলিশা, আপনি কি নিশ্চিত ?. docs.oracle.com/javase/8/docs/api/java/util/concurrent/… বলছে একটি সুশৃঙ্খলভাবে শাটডাউন শুরু করা যাতে পূর্বে জমা দেওয়া কাজগুলি কার্যকর করা হয়, তবে কোনও নতুন কাজ গ্রহণ করা হবে না।
জাইমে হাবলুটজেল

7

আপনি একটি নির্দিষ্ট বিরতিতে কাজ শেষ করতে অপেক্ষা করতে পারেন:

int maxSecondsPerComputeDTask = 20;
try {
    while (!es.awaitTermination(uniquePhrases.size() * maxSecondsPerComputeDTask, TimeUnit.SECONDS)) {
        // consider giving up with a 'break' statement under certain conditions
    }
} catch (InterruptedException e) {
    throw new RuntimeException(e);    
}

অথবা আপনি এক্সিকিউটর সার্ভিস ব্যবহার করতে পারেন । জমা দিন ( চলমান ) এবং ভবিষ্যতের অবজেক্টগুলি যা এটি ফিরে আসে তা সংগ্রহ করুন এবং তাদের সমাপ্তির জন্য অপেক্ষা করার জন্য প্রতিটিকে get () কল করুন।

ExecutorService es = Executors.newFixedThreadPool(2);
Collection<Future<?>> futures = new LinkedList<<Future<?>>();
for (DataTable singleTable : uniquePhrases) {
    futures.add(es.submit(new ComputeDTask(singleTable)));
}
for (Future<?> future : futures) {
   try {
       future.get();
   } catch (InterruptedException e) {
       throw new RuntimeException(e);
   } catch (ExecutionException e) {
       throw new RuntimeException(e);
   }
}

বাধা বিপত্তিটি সঠিকভাবে পরিচালনা করার জন্য অত্যন্ত গুরুত্বপূর্ণ। এটি আপনাকে বা আপনার গ্রন্থাগারের ব্যবহারকারীদের একটি দীর্ঘ প্রক্রিয়া নিরাপদে শেষ করতে দেয়।


6

শুধু ব্যবহার

latch = new CountDownLatch(noThreads)

প্রতিটি থ্রেডে

latch.countDown();

এবং বাধা হিসাবে

latch.await();

6

অবৈধমনিটর স্টেট এক্সেকশন এর মূল কারণ :

থ্রেডটি নির্দিষ্ট মনিটরের মালিকানা ছাড়াই কোনও বস্তুর মনিটরের জন্য অপেক্ষা করার বা অন্য থ্রেডকে অবহিত করার জন্য চেষ্টা করেছে তা বোঝাতে নিক্ষিপ্ত হয়েছিল।

আপনার কোড থেকে, আপনি লকটির মালিকানা ছাড়াই এক্সিকিউটর সার্ভিসে সবেমাত্র অপেক্ষা () বলেছিলেন।

নীচে কোড ঠিক করা হবে IllegalMonitorStateException

try 
{
    synchronized(es){
        es.wait(); // Add some condition before you call wait()
    }
} 

জমা দেওয়া সমস্ত কার্য সমাপ্তির জন্য অপেক্ষা করতে নীচের একটি পদ্ধতির অনুসরণ করুন ExecutorService

  1. পুনরুক্তি সব দিয়ে Futureথেকে কর্ম submitউপর ExecutorServiceকল ব্লক ও স্থিতি পরীক্ষা get()উপর Futureবস্তু

  2. ইনভোকএল সব ব্যবহার করেExecutorService

  3. কাউন্টডাউনল্যাচ ব্যবহার করা

  4. ব্যবহার ForkJoinPool বা newWorkStealingPool এর Executors(জাভা 8 থেকে)

  5. ওরাকল ডকুমেন্টেশন পৃষ্ঠায় প্রস্তাবিত পুলটি বন্ধ করুন

    void shutdownAndAwaitTermination(ExecutorService pool) {
       pool.shutdown(); // Disable new tasks from being submitted
       try {
       // Wait a while for existing tasks to terminate
       if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
           pool.shutdownNow(); // Cancel currently executing tasks
           // Wait a while for tasks to respond to being cancelled
           if (!pool.awaitTermination(60, TimeUnit.SECONDS))
           System.err.println("Pool did not terminate");
       }
    } catch (InterruptedException ie) {
         // (Re-)Cancel if current thread also interrupted
         pool.shutdownNow();
         // Preserve interrupt status
         Thread.currentThread().interrupt();
    }

    আপনি যখন 1 থেকে 4 বিকল্পের পরিবর্তে বিকল্প 5 ব্যবহার করছেন তখন আপনি যদি সমস্ত কাজ সমাপ্তির জন্য করুণার সাথে অপেক্ষা করতে চান তবে পরিবর্তন করুন

    if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {

    প্রতি

    একটি while(condition)যা প্রতি 1 মিনিটের জন্য পরীক্ষা করে।


6

আপনি ExecutorService.invokeAllপদ্ধতিটি ব্যবহার করতে পারেন , এটি সমস্ত কার্য সম্পাদন করে এবং সমস্ত থ্রেড তাদের কাজ শেষ না হওয়া পর্যন্ত অপেক্ষা করবে।

এখানে সম্পূর্ণ জাভাদোক

সময়সীমাটি নির্দিষ্ট করতে আপনি এই পদ্ধতির ওভারলোড হওয়া সংস্করণটিও ব্যবহারকারী করতে পারেন।

এখানে নমুনা কোড সহ ExecutorService.invokeAll

public class Test {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService service = Executors.newFixedThreadPool(3);
        List<Callable<String>> taskList = new ArrayList<>();
        taskList.add(new Task1());
        taskList.add(new Task2());
        List<Future<String>> results = service.invokeAll(taskList);
        for (Future<String> f : results) {
            System.out.println(f.get());
        }
    }

}

class Task1 implements Callable<String> {
    @Override
    public String call() throws Exception {
        try {
            Thread.sleep(2000);
            return "Task 1 done";
        } catch (Exception e) {
            e.printStackTrace();
            return " error in task1";
        }
    }
}

class Task2 implements Callable<String> {
    @Override
    public String call() throws Exception {
        try {
            Thread.sleep(3000);
            return "Task 2 done";
        } catch (Exception e) {
            e.printStackTrace();
            return " error in task2";
        }
    }
}

3

আমার এমন অবস্থাও আছে যে আমার কাছে ক্রল করার জন্য একটি নথিপত্র সেট রয়েছে। আমি একটি প্রারম্ভিক "বীজ" নথিটি দিয়ে শুরু করি যা প্রক্রিয়া করা উচিত, সেই নথিতে অন্যান্য নথির লিঙ্ক রয়েছে যাগুলিও প্রক্রিয়া করা উচিত, ইত্যাদি।

আমার মূল প্রোগ্রামে আমি কেবল নীচের মতো কিছু লিখতে চাই যেখানে Crawlerথ্রেডগুলির একগুচ্ছ নিয়ন্ত্রণ করে।

Crawler c = new Crawler();
c.schedule(seedDocument); 
c.waitUntilCompletion()

একই রকম পরিস্থিতি ঘটবে যদি আমি একটি গাছ নেভিগেট করতে চাই; আমি রুট নোডে পপ করব, প্রতিটি নোডের প্রসেসর শিশুদের প্রয়োজনীয়ভাবে কাতারে যুক্ত করত এবং থ্রেডের একগুচ্ছ গাছের সমস্ত নোডগুলি প্রক্রিয়া করত, যতক্ষণ না আর কিছু ছিল না।

আমি জেভিএম-তে এমন কিছু খুঁজে পাইনি যা আমি ভেবেছিলাম কিছুটা অবাক হয়েছিল। সুতরাং আমি এমন একটি ক্লাস লিখেছিলাম ThreadPoolযা প্রত্যক্ষ বা সাবক্লাস ব্যবহার করে ডোমেনের উপযোগী পদ্ধতিগুলি যুক্ত করতে পারে, যেমন schedule(Document)। আশা করি এটা সাহায্য করবে!

থ্রেডপুল জাভাদোক | ম্যাভেন


ডক লিঙ্কটি মারা গেছে
মান্টি_ কোর

@ মন্টি_কোর - ধন্যবাদ, আপডেট হয়েছে।
অ্যাড্রিয়ান স্মিথ

2

সংগ্রহের সমস্ত থ্রেড যুক্ত করুন এবং এটি ব্যবহার করে জমা দিন invokeAll। আপনি যদি এর invokeAllপদ্ধতি ব্যবহার করতে পারেন ExecutorService, সমস্ত থ্রেড সম্পূর্ণ না হওয়া পর্যন্ত JVM পরবর্তী লাইনে অগ্রসর হবে না।

এখানে একটি ভাল উদাহরণ রয়েছে: এক্সিকিউটর সার্ভিসের মাধ্যমে চালিত সমস্ত


1

আপনার কর্ম জমা দিন রানার এবং তারপর পদ্ধতি কলিং অপেক্ষা করুন waitTillDone () ভালো:

Runner runner = Runner.runner(2);

for (DataTable singleTable : uniquePhrases) {

    runner.run(new ComputeDTask(singleTable));
}

// blocks until all tasks are finished (or failed)
runner.waitTillDone();

runner.shutdown();

এটি ব্যবহার করতে এই গ্রেড / মাভেন নির্ভরতা যুক্ত করুন: 'com.github.matejtymes:javafixes:1.0'

আরও বিশদের জন্য এখানে দেখুন: https://github.com/MatejTymes/ জাভা ফিক্স বা এখানে:



0

আমি কেবল নির্বাহকের নির্দিষ্ট সময়সীমার সাথে সমাপ্ত হওয়ার জন্য অপেক্ষা করব যা আপনি মনে করেন যে এটি কাজগুলি সম্পূর্ণ করার পক্ষে উপযুক্ত।

 try {  
         //do stuff here 
         exe.execute(thread);
    } finally {
        exe.shutdown();
    }
    boolean result = exe.awaitTermination(4, TimeUnit.HOURS);
    if (!result)

    {
        LOGGER.error("It took more than 4 hour for the executor to stop, this shouldn't be the normal behaviour.");
    }

0

আপনার প্রয়োজন মত মনে হচ্ছে ForkJoinPoolএবং কার্য সম্পাদন করতে গ্লোবাল পুল ব্যবহার করুন।

public static void main(String[] args) {
    // the default `commonPool` should be sufficient for many cases.
    ForkJoinPool pool = ForkJoinPool.commonPool(); 
    // The root of your task that may spawn other tasks. 
    // Make sure it submits the additional tasks to the same executor that it is in.
    Runnable rootTask = new YourTask(pool); 
    pool.execute(rootTask);
    pool.awaitQuiescence(...);
    // that's it.
}

সৌন্দর্যটি pool.awaitQuiescenceযেখানে পদ্ধতিটি কলারের থ্রেডটিকে তার কাজগুলি সম্পাদন করতে ব্লক করবে এবং তারপরে এটি সত্যই শূন্য হলে ফিরে আসবে ।

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