সমস্ত থ্রেড জাভাতে তাদের কাজ শেষ না হওয়া পর্যন্ত অপেক্ষা করুন


94

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


4
থ্রেড.জাইন সমস্যাটি সমাধান করার জন্য একটি নিম্ন স্তরের খুব জাভা আইডিয়োসিনক্র্যাটিক উপায়। তবুও এটি সমস্যাযুক্ত কারণ থ্রেড এপিআই ত্রুটিযুক্ত: আপনি যোগদান করতে পারবেন কিনা তা সফলভাবে সফলভাবে সম্পন্ন হয়েছে কিনা তা আপনি জানতে পারবেন না ( অনুশীলনে জাভা কনকুরન્સી দেখুন )। কাউন্টডাউনল্যাচ ব্যবহারের মতো উচ্চ স্তরের বিমূর্ততা পছন্দনীয় হতে পারে এবং জাভা-আইডিয়োসিনক্র্যাটিক মানসিকতায় "আটকে না" এমন প্রোগ্রামারদের কাছে এটি আরও প্রাকৃতিক দেখায়। আমার সাথে তর্ক করবেন না, ডগ লিয়ার সাথে তর্ক করুন; )
সিড্রিক মার্টিন

উত্তর:


122

থ্রেডের পুলগুলি পরিচালনা করতে আমি এক্সিকিউটারসেবাড়ি ব্যবহার করাই আমি গ্রহণ করি ।

ExecutorService es = Executors.newCachedThreadPool();
for(int i=0;i<5;i++)
    es.execute(new Runnable() { /*  your task */ });
es.shutdown();
boolean finished = es.awaitTermination(1, TimeUnit.MINUTES);
// all tasks have finished or the time has been reached.

7
@ লিওনিড এটি হ'ল শাটডাউন () করে।
পিটার লরি

4
while(!es.awaitTermination(1, TimeUnit.MINUTES));
কুম্ভ শক্তি

4
@ অ্যাকুরিয়াস পাওয়ারটি আপনি কেবল এটি আরও দীর্ঘকাল বা চিরকাল অপেক্ষা করতে বলতে পারেন।
পিটার লরি

4
ওহ আমি এটা বুঝতে পেরেছি; সুতরাং আমি লুপে একটি বার্তা যুক্ত করেছি যে এটি সমস্ত থ্রেড শেষ হওয়ার অপেক্ষায় রয়েছে; ধন্যবাদ!
কুম্ভ শক্তি

4
@ পিটারলাউরে, ফোন করা কি দরকার es.shutdown();? কি যদি আমি একটি কোড যা আমি একটি থ্রেড ব্যবহার মৃত্যুদন্ড কার্যকর লিখতে es.execute(runnableObj_ZipMaking);মধ্যে tryব্লক এবং finallyআমি নামক boolean finshed = es.awaitTermination(10, TimeUnit.MINUTES);। সুতরাং আমি মনে করি যে সমস্ত থ্রেডগুলি তাদের কাজ শেষ না করা বা সময়সীমা শেষ হওয়ার আগেই অপেক্ষা করা উচিত (যা যা আগে হয়) আমার ধারণাটি কি সঠিক? বা কল করা shutdown()বাধ্যতামূলক?
আমোগ

54

আপনি joinথ্রেড করতে পারেন । থ্রেডটি সম্পূর্ণ না হওয়া পর্যন্ত জোড় ব্লকগুলি।

for (Thread thread : threads) {
    thread.join();
}

নোট করুন যে joinএকটি নিক্ষেপ InterruptedException। যদি তা ঘটে থাকে তবে আপনাকে কী করতে হবে তা সিদ্ধান্ত নিতে হবে (উদাঃ অপ্রয়োজনীয় কাজ করা রোধ করার জন্য অন্যান্য থ্রেডগুলি বাতিল করার চেষ্টা করুন)।


4
এই থ্রেডগুলি কি একে অপরের সমান্তরাল বা ক্রমান্বয়ে চালিত হয়?
জেমস ওয়েবস্টার

4
@ জামেস ওয়েস্টার: সমান্তরাল।
আরওয়াইএন

4
@ জেমস ওয়েবস্টার: বিবৃতিটির t.join();অর্থ হ'ল থ্রেড শেষ না হওয়া অবধি বর্তমান থ্রেডটি ব্লক করে t। এটি থ্রেডকে প্রভাবিত করে না t
মার্ক Byers

4
ধন্যবাদ =] ইউনিতে প্যারালাইলিজম অধ্যয়ন করেছেন, তবে এটাই ছিল আমি শেখার লড়াইয়ে একমাত্র লড়াই! ধন্যবাদ আমি এখনই এটি বেশি ব্যবহার করতে হবে না বা যখন করি এটি খুব জটিল নয় বা কোনও ভাগ করা সংস্থান নেই এবং অবরুদ্ধকরণ গুরুতর নয়
জেমস ওয়েবস্টার

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

22

বিভিন্ন সমাধান দেখুন।

  1. join()জাভার প্রাথমিক সংস্করণগুলিতে এপিআই চালু করা হয়েছে। জেডি কে 1.5 প্রকাশের পর থেকে এই সমবর্তী প্যাকেজের সাথে কিছু ভাল বিকল্প উপলব্ধ ।

  2. এক্সিকিউটর সার্ভিস # ইনভোকএল ()

    প্রদত্ত টাস্কগুলি কার্যকর করে ফিউচারগুলির একটি স্ট্যাটাস এবং সমস্ত কিছু সম্পন্ন হওয়ার পরে ফলাফলের তালিকা ফিরিয়ে দেয়।

    কোড উদাহরণের জন্য এই সম্পর্কিত এসই প্রশ্নটি দেখুন:

    সমস্ত থ্রেড পুলকে তাদের কাজটি করার জন্য কীভাবে ইনভোকএল () ব্যবহার করবেন?

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

    একটি সিঙ্ক্রোনাইজেশন সহায়তা যা অন্য থ্রেডে পরিচালিত ক্রিয়াকলাপের সেট সম্পূর্ণ না হওয়া পর্যন্ত এক বা একাধিক থ্রেড অপেক্ষা করতে দেয় allows

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

    ব্যবহারের জন্য এই প্রশ্নটি দেখুন CountDownLatch

    এমন থ্রেডের জন্য কীভাবে অপেক্ষা করতে হবে যা এটি নিজের থ্রেডকে ছড়িয়ে দিয়েছে?

  4. ForkJoinPool বা newWorkStealingPool () মধ্যে নির্বাহক

  5. জমা দেওয়ার পরে তৈরি সমস্ত ফিউচার অবজেক্টের মাধ্যমে আইট্রেট করুনExecutorService


11

Thread.join()অন্যদের পরামর্শ ছাড়াও , জাভা 5 এক্সিকিউটারের কাঠামোটি চালু করেছিল। সেখানে আপনি Threadবস্তুর সাথে কাজ করবেন না । পরিবর্তে, আপনি নিজের Callableবা Runnableবস্তুগুলি একজন নির্বাহকের কাছে জমা দিন এখানে একটি বিশেষ নির্বাহক রয়েছে যার অর্থ একাধিক টাস্ক সম্পাদন করা এবং তাদের ফলাফলগুলি ক্রমবর্ধমান। এটাই ExecutorCompletionService:

ExecutorCompletionService executor;
for (..) {
    executor.submit(Executors.callable(yourRunnable));
}

তারপরে ফিরে আসার মতো take()আর কোনও Future<?>অবজেক্ট না পাওয়া পর্যন্ত আপনি বারবার কল করতে পারবেন , যার অর্থ এটি সমস্ত শেষ হয়ে গেছে।


আপনার দৃশ্যের উপর নির্ভর করে আরেকটি জিনিস প্রাসঙ্গিক হতে পারে CyclicBarrier

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


এটি নিকটে, তবে আমি এখনও বেশ কয়েকটি সমন্বয় করব। executor.submitফেরত a Future<?>। আমি এই ফিউচারগুলিকে একটি তালিকায় যুক্ত করব এবং তারপরে getপ্রতিটি ভবিষ্যতের জন্য তালিকার মধ্য দিয়ে লুপ করব ।
রায়

এছাড়াও, আপনি কোনও নির্মাণকারীর ইনস্ট্যান্টিয়েট করতে পারেন Executors, উদাহরণস্বরূপ, Executors.newCachedThreadPool(বা অনুরূপ)
রায়

10

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

মনোযোগ দিন যে এই বস্তুর সাথে, একটি InterruptedExceptionপাশাপাশি নিক্ষেপ করা যেতে পারে।


8

কিছু অন্যান্য থ্রেডের কাজ শেষ না হওয়া পর্যন্ত থ্রেড মেইনটি অপেক্ষা করুন / অবরুদ্ধ করুন।

যেমনটি @Ravindra babuবলা হয়েছে এটি বিভিন্ন উপায়ে অর্জন করা যেতে পারে তবে উদাহরণ দিয়ে দেখানো হচ্ছে।

  • java.lang.Thread। যোগদান () যেহেতু: 1.0

    public static void joiningThreads() throws InterruptedException {
        Thread t1 = new Thread( new LatchTask(1, null), "T1" );
        Thread t2 = new Thread( new LatchTask(7, null), "T2" );
        Thread t3 = new Thread( new LatchTask(5, null), "T3" );
        Thread t4 = new Thread( new LatchTask(2, null), "T4" );
    
        // Start all the threads
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        // Wait till all threads completes
        t1.join();
        t2.join();
        t3.join();
        t4.join();
    }
    
  • java.util.concurrent.CountDownLatch থেকে: 1.5

    • .countDown() Lat ল্যাচ গ্রুপের গণনা হ্রাস।
    • .await() Count বর্তমান গণনা শূন্য না হওয়া পর্যন্ত অপেক্ষা করা পদ্ধতিগুলি অবরুদ্ধ থাকে।

    যদি আপনি তৈরি করেন latchGroupCount = 4তবে countDown()4 বার গণনা করতে 4 বার কল করা উচিত So সুতরাং, await()এটি ব্লকিং থ্রেড প্রকাশ করবে।

    public static void latchThreads() throws InterruptedException {
        int latchGroupCount = 4;
        CountDownLatch latch = new CountDownLatch(latchGroupCount);
        Thread t1 = new Thread( new LatchTask(1, latch), "T1" );
        Thread t2 = new Thread( new LatchTask(7, latch), "T2" );
        Thread t3 = new Thread( new LatchTask(5, latch), "T3" );
        Thread t4 = new Thread( new LatchTask(2, latch), "T4" );
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        //latch.countDown();
    
        latch.await(); // block until latchGroupCount is 0.
    }
    

থ্রেডেড ক্লাসের উদাহরণ কোড LatchTask। পদ্ধতির ব্যবহার joiningThreads(); এবং latchThreads();প্রধান পদ্ধতি থেকে পরীক্ষা করতে ।

class LatchTask extends Thread {
    CountDownLatch latch;
    int iterations = 10;
    public LatchTask(int iterations, CountDownLatch latch) {
        this.iterations = iterations;
        this.latch = latch;
    }

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " : Started Task...");

        for (int i = 0; i < iterations; i++) {
            System.out.println(threadName + " : " + i);
            MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
        }
        System.out.println(threadName + " : Completed Task");
        // countDown() « Decrements the count of the latch group.
        if(latch != null)
            latch.countDown();
    }
}
  • সাইক্লিক ব্যারিয়ারস একটি সিঙ্ক্রোনাইজেশন সহায়তা যা থ্রেডের একটি সেটকে একে অপরের জন্য একটি সাধারণ বাধা বিন্দুতে পৌঁছানোর জন্য অপেক্ষা করে। সাইক্লিক ব্যারিয়ারগুলি থ্রেডগুলির একটি নির্দিষ্ট আকারের পার্টির সাথে জড়িত প্রোগ্রামগুলিতে দরকারী যেগুলি মাঝে মধ্যে একে অপরের জন্য অপেক্ষা করতে হবে। বাধাটিকে সাইক্লিক বলা হয় কারণ অপেক্ষার থ্রেডগুলি প্রকাশের পরে এটি আবার ব্যবহার করা যেতে পারে।
    CyclicBarrier barrier = new CyclicBarrier(3);
    barrier.await();
    
    উদাহরণস্বরূপ, এই সমকালীন_পরি متাল নোটাইফাই ক্লাসটি উল্লেখ করুন।

  • এক্সিকিউটার কাঠামো: আমরা একটি থ্রেড পুল তৈরি করতে এক্সিকিউটর সার্ভিস ব্যবহার করতে পারি এবং ভবিষ্যতের সাথে অ্যাসিক্রোনাস কার্যগুলির অগ্রগতি ট্র্যাক করি।

    • submit(Runnable), submit(Callable)যা ফিউচার অবজেক্ট ফেরত দেয়। future.get()ফাংশন ব্যবহার করে আমরা কার্যত থ্রেডগুলি এর কাজ শেষ না হওয়া পর্যন্ত মূল থ্রেডটি ব্লক করতে পারি।

    • invokeAll(...) - ভবিষ্যতের অবজেক্টগুলির একটি তালিকা ফেরত দেয় যার মাধ্যমে আপনি প্রতিটি কলযোগ্য এর ফাঁসি কার্যকর করতে পারেন।

ইন্টারফেস চালানো, এক্সিকিউটার ফ্রেমওয়ার্ক সহ কলযোগ্য ব্যবহার করার উদাহরণ সন্ধান করুন


@আরো দেখুন


7

তুমি কর

for (Thread t : new Thread[] { th1, th2, th3, th4, th5 })
    t.join()

লুপের জন্য এটির পরে, আপনি নিশ্চিত হতে পারেন যে সমস্ত থ্রেড তাদের কাজ শেষ করেছে।


4

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



2

যদিও ওপি-র সমস্যার সাথে প্রাসঙ্গিক নয়, আপনি যদি ঠিক একটি থ্রেড সহ সিঙ্ক্রোনাইজেশন (আরও স্পষ্টভাবে, একটি রেন্ডিজ-ভৌস) করতে আগ্রহী হন তবে আপনি এক্সচেঞ্জার ব্যবহার করতে পারেন

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


1

একজন নির্বাহক পরিষেবা স্থিতি এবং সমাপ্তি সহ একাধিক থ্রেড পরিচালনা করতে ব্যবহৃত হতে পারে। দেখুন http://programmingexferences.wikidot.com/executorservice


1

এটি চেষ্টা করুন, কাজ করবে।

  Thread[] threads = new Thread[10];

  List<Thread> allThreads = new ArrayList<Thread>();

  for(Thread thread : threads){

        if(null != thread){

              if(thread.isAlive()){

                    allThreads.add(thread);

              }

        }

  }

  while(!allThreads.isEmpty()){

        Iterator<Thread> ite = allThreads.iterator();

        while(ite.hasNext()){

              Thread thread = ite.next();

              if(!thread.isAlive()){

                   ite.remove();
              }

        }

   }

1

আমি একই সমস্যা ছিল এবং জাভা 8 সমান্তরাল স্ট্রিম ব্যবহার করে শেষ করেছি।

requestList.parallelStream().forEach(req -> makeRequest(req));

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

সমান্তরাল স্ট্রিমগুলি সম্পর্কে এখানে আরও তথ্য ।


1

আমি কয়েকটি থ্রেড সমাপ্ত হওয়ার জন্য অপেক্ষা করতে একটি ছোট সহায়ক সাহায্যকারী পদ্ধতি তৈরি করেছি:

public static void waitForThreadsToFinish(Thread... threads) {
        try {
            for (Thread thread : threads) {
                thread.join();
            }
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

0

বিদ্যমান উত্তর join()প্রতিটি থ্রেড পারে বলেছিলেন ।

তবে থ্রেড অ্যারে / তালিকা পাওয়ার বিভিন্ন উপায় রয়েছে:

  • থ্রেডটি তৈরির তালিকায় যুক্ত করুন।
  • ThreadGroupথ্রেড পরিচালনা করতে ব্যবহার করুন ।

নিম্নলিখিত কোডটি ThreadGruopপদ্ধতির ব্যবহার করবে । এটি প্রথমে একটি গ্রুপ তৈরি করে, তারপরে প্রতিটি থ্রেড তৈরি করার পরে কনস্ট্রাক্টরে গ্রুপটি নির্দিষ্ট করে, পরবর্তীতে থ্রেড অ্যারেটি পেতে পারেThreadGroup.enumerate()


কোড

সিঙ্কব্লকলায়ার্ন.জভা

import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * synchronized block - learn,
 *
 * @author eric
 * @date Apr 20, 2015 1:37:11 PM
 */
public class SyncBlockLearn {
    private static final int TD_COUNT = 5; // thread count
    private static final int ROUND_PER_THREAD = 100; // round for each thread,
    private static final long INC_DELAY = 10; // delay of each increase,

    // sync block test,
    @Test
    public void syncBlockTest() throws InterruptedException {
        Counter ct = new Counter();
        ThreadGroup tg = new ThreadGroup("runner");

        for (int i = 0; i < TD_COUNT; i++) {
            new Thread(tg, ct, "t-" + i).start();
        }

        Thread[] tArr = new Thread[TD_COUNT];
        tg.enumerate(tArr); // get threads,

        // wait all runner to finish,
        for (Thread t : tArr) {
            t.join();
        }

        System.out.printf("\nfinal count: %d\n", ct.getCount());
        Assert.assertEquals(ct.getCount(), TD_COUNT * ROUND_PER_THREAD);
    }

    static class Counter implements Runnable {
        private final Object lkOn = new Object(); // the object to lock on,
        private int count = 0;

        @Override
        public void run() {
            System.out.printf("[%s] begin\n", Thread.currentThread().getName());

            for (int i = 0; i < ROUND_PER_THREAD; i++) {
                synchronized (lkOn) {
                    System.out.printf("[%s] [%d] inc to: %d\n", Thread.currentThread().getName(), i, ++count);
                }
                try {
                    Thread.sleep(INC_DELAY); // wait a while,
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.printf("[%s] end\n", Thread.currentThread().getName());
        }

        public int getCount() {
            return count;
        }
    }
}

মূল থ্রেডটি গ্রুপের সমস্ত থ্রেড সমাপ্ত হওয়ার জন্য অপেক্ষা করবে।


-1

এটি আপনার মূল থ্রেডে ব্যবহার করুন: যখন (! Executor.isTerminated ()); এক্সিকিউটার পরিষেবা থেকে সমস্ত থ্রেড শুরু করার পরে কোডের এই লাইনটি রাখুন। এক্সিকিউটররা দ্বারা শুরু সমস্ত থ্রেড সমাপ্ত হওয়ার পরে এটি কেবল মূল থ্রেডটি শুরু করবে। এক্সিকিউটার.শুটডাউন () কল করতে ভুলবেন না; উপরের লুপের আগে


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