আমি কীভাবে থ্রেডগুলির মধ্যে ব্যতিক্রমগুলি প্রচার করতে পারি?


105

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

ফলাফলটি হ'ল কলার নির্বিকারভাবে ফাংশনটি ব্যবহার করতে পারবেন এবং অভ্যন্তরীণভাবে এটি একাধিক কোর ব্যবহার করবে।

এখন পর্যন্ত সব ভাল ..

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

এটা আমরা কিভাবে করতে পারি?

আমি সবচেয়ে ভাল চিন্তা করতে পারি:

  1. আমাদের কর্মী থ্রেডগুলিতে সম্পূর্ণ ব্যতিক্রমগুলি ধরুন (স্ট্যান্ডার্ড :: ব্যতিক্রম এবং আমাদের নিজস্ব কয়েকটি)।
  2. ব্যতিক্রমের ধরণ এবং বার্তাটি রেকর্ড করুন।
  3. মূল থ্রেডের সাথে সম্পর্কিত স্যুইচ স্টেটমেন্ট রয়েছে যা শ্রমিকের থ্রেডে যা কিছু রেকর্ড করা হয়েছিল তার ব্যাতিক্রমগুলিকে প্রত্যাখ্যান করে।

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

উত্তর:


89

সি ++ 11 exception_ptrপ্রকারটি প্রবর্তন করে যা থ্রেডগুলির মধ্যে ব্যতিক্রমগুলি পরিবহন করতে দেয়:

#include<iostream>
#include<thread>
#include<exception>
#include<stdexcept>

static std::exception_ptr teptr = nullptr;

void f()
{
    try
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        throw std::runtime_error("To be passed between threads");
    }
    catch(...)
    {
        teptr = std::current_exception();
    }
}

int main(int argc, char **argv)
{
    std::thread mythread(f);
    mythread.join();

    if (teptr) {
        try{
            std::rethrow_exception(teptr);
        }
        catch(const std::exception &ex)
        {
            std::cerr << "Thread exited with exception: " << ex.what() << "\n";
        }
    }

    return 0;
}

কারণ আপনার ক্ষেত্রে আপনার একাধিক কর্মী থ্রেড রয়েছে, আপনার exception_ptrপ্রত্যেকটির জন্য একটি করে রাখা দরকার ।

নোটটি exception_ptrএকটি ভাগ করা পিআরটি-এর মতো পয়েন্টার, সুতরাং আপনাকে exception_ptrপ্রতিটি ব্যতিক্রমের জন্য কমপক্ষে একটি পয়েন্ট করাতে হবে বা সেগুলি মুক্তি পাবে।

মাইক্রোসফ্ট সুনির্দিষ্ট: আপনি যদি এসইএইচ ব্যতিক্রম ( /EHa) ব্যবহার করেন তবে উদাহরণ কোড অ্যাক্সেস লঙ্ঘনের মতো এসইএইচ ব্যতিক্রম পরিবহন করবে, যা আপনি চান তা নাও হতে পারে।


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

75

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

সি ++ 0x এ আপনি কোনও ব্যতিক্রম ধরতে সক্ষম হবেন catch(...)এবং তারপরে এটি std::exception_ptrব্যবহারের ক্ষেত্রে সংরক্ষণ করতে পারবেন std::current_exception()। তারপরে আপনি এটি একই বা অন্য কোনও থ্রেড দিয়ে পরে পুনর্বিবেচনা করতে পারেন std::rethrow_exception()

আপনি যদি মাইক্রোসফ্ট ভিজ্যুয়াল স্টুডিও 2005 বা তার পরে ব্যবহার করেন তবে কেবল :: থ্রেড সি ++ 0x থ্রেড লাইব্রেরি সমর্থন করে std::exception_ptr। (অস্বীকৃতি: এটি আমার পণ্য)।


7
এটি এখন সি ++ 11 এর অংশ এবং এমএসভিএস 2010 দ্বারা সমর্থিত; দেখতে msdn.microsoft.com/en-us/library/dd293602.aspx
জোহান রাদে

7
এটি লিনাক্সে জিসিসি 4.4+ দ্বারা সমর্থিত।
অ্যান্টনি উইলিয়ামস

দুর্দান্ত, ব্যবহারের উদাহরণের জন্য এখানে লিঙ্ক রয়েছে: en.cppreferences.com/w/cpp/error/exception_ptr
অ্যালেক্সিস উইলক

11

আপনি যদি সি ++ 11 ব্যবহার করছেন তবে আপনি যা যা সন্ধান করছেন std::futureঠিক তা করতে পারে: এটি স্বয়ংক্রিয়ভাবে ব্যতিক্রমগুলি আটকাতে পারে যা এটিকে কর্মী থ্রেডের শীর্ষে পরিণত করে এবং সেটিকে মূল পয়েন্টের মূল পয়েন্টের মধ্য দিয়ে যেতে পারে std::future::getis বলা হয়। (পর্দার আড়ালে, এটি @ অ্যান্টনিউইলিয়ামস এর উত্তরের মতো ঠিক ঘটে; এটি ইতিমধ্যে আপনার জন্য কার্যকর করা হয়েছে।)

নীচের দিকটি হ'ল "যত্ন নেওয়া বন্ধ" ক এর কোনও আদর্শ উপায় নেই std::future; এমনকি এর ডেস্ট্রাক্টর কাজটি শেষ না হওয়া পর্যন্ত কেবল ব্লক করে দেবে। [সম্পাদনা, 2017: ব্লকিং-ডেস্ট্রাক্টর আচরণটি কেবলমাত্র সিউডো ফিউচারগুলির থেকে একটি ভুল অপব্যবহার std::async, যা আপনার কখনই ব্যবহার করা উচিত নয়। সাধারণ ফিউচারগুলি তাদের ধ্বংসকারীদের মধ্যে অবরুদ্ধ হয় না। তবে আপনি যদি এখনও ব্যবহার করছেন তবে "বাতিল" কাজগুলি করতে পারবেন না std::future: প্রতিশ্রুতি পূরণকারী টাস্কগুলি পর্দার আড়ালে চালিয়ে যেতে থাকবে এমনকি কেউ যদি আর উত্তর না শুনছে তবে।] এখানে একটি খেলনার উদাহরণ যা স্পষ্ট করে দিতে পারে আমি কি বলতে চান:

#include <atomic>
#include <chrono>
#include <exception>
#include <future>
#include <thread>
#include <vector>
#include <stdio.h>

bool is_prime(int n)
{
    if (n == 1010) {
        puts("is_prime(1010) throws an exception");
        throw std::logic_error("1010");
    }
    /* We actually want this loop to run slowly, for demonstration purposes. */
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    for (int i=2; i < n; ++i) { if (n % i == 0) return false; }
    return (n >= 2);
}

int worker()
{
    static std::atomic<int> hundreds(0);
    const int start = 100 * hundreds++;
    const int end = start + 100;
    int sum = 0;
    for (int i=start; i < end; ++i) {
        if (is_prime(i)) { printf("%d is prime\n", i); sum += i; }
    }
    return sum;
}

int spawn_workers(int N)
{
    std::vector<std::future<int>> waitables;
    for (int i=0; i < N; ++i) {
        std::future<int> f = std::async(std::launch::async, worker);
        waitables.emplace_back(std::move(f));
    }

    int sum = 0;
    for (std::future<int> &f : waitables) {
        sum += f.get();  /* may throw an exception */
    }
    return sum;
    /* But watch out! When f.get() throws an exception, we still need
     * to unwind the stack, which means destructing "waitables" and each
     * of its elements. The destructor of each std::future will block
     * as if calling this->wait(). So in fact this may not do what you
     * really want. */
}

int main()
{
    try {
        int sum = spawn_workers(100);
        printf("sum is %d\n", sum);
    } catch (std::exception &e) {
        /* This line will be printed after all the prime-number output. */
        printf("Caught %s\n", e.what());
    }
}

আমি কেবল ব্যবহার করে একটি কাজের মতো উদাহরণ লেখার চেষ্টা করেছি std::threadএবং std::exception_ptrতবে কিছু ভুল হয়ে যাচ্ছে std::exception_ptr(libc ++ ব্যবহার করে) সুতরাং আমি এটি এখনও কাজ করতে পারিনি। :(

[সম্পাদনা, 2017:

int main() {
    std::exception_ptr e;
    std::thread t1([&e](){
        try {
            ::operator new(-1);
        } catch (...) {
            e = std::current_exception();
        }
    });
    t1.join();
    try {
        std::rethrow_exception(e);
    } catch (const std::bad_alloc&) {
        puts("Success!");
    }
}

২০১৩ সালে আমি কী ভুল করেছিলাম সে সম্পর্কে আমার কোনও ধারণা নেই তবে আমি নিশ্চিত যে এটি আমার ভুল ছিল]


আপনি কেন সৃষ্টিকে ভবিষ্যতে কোনও নাম দেওয়া fএবং তারপরে বরাদ্দ করেন emplace_back? আপনি কি করতে পারবেন না waitables.push_back(std::async(…));বা আমি কিছু উপেক্ষা করছি (এটি সংকলন করে, প্রশ্নটি এটি ফাঁস হতে পারে কিনা, তবে কীভাবে দেখছি না)?
কনরাড রুডলফ

1
এছাড়াও, ফিউচারগুলিকে আইংয়ের পরিবর্তে বাতিল করে স্ট্যাকটি আনওয়াইন্ডিং করার কোনও উপায় আছে wait? "কাজগুলির মধ্যে একটি ব্যর্থ হওয়ার সাথে সাথে অন্যরা কোনও বিষয় বিবেচনা করে না" লাইন ধরে কিছু।
কনরাড রুডলফ

৪ বছর পরে, আমার উত্তরটির বয়স ভাল হয়নি। :) পুনরায় "কেন": আমি মনে করি এটি কেবল স্পষ্টতার জন্য ছিল (এটি দেখানোর জন্য যে asyncকোনও কিছু-কিছু না করে ভবিষ্যতের প্রত্যাবর্তন করে)। পুনরায় "এছাড়াও, রয়েছে": এটিকে বাস্তবায়িত করার বিভিন্ন উপায়ের জন্য std::futureশন পিতামাতার আলাপ "বেটার কোড: কনকুরન્સી" বা আমার "ফিউচারস ফ্রি স্ক্র্যাচ" দেখুন যে আপনি যদি শুরু করতে পুরো এসটিএলকে আবার লিখতে আপত্তি করেন না। :) মূল অনুসন্ধান শব্দটি হল "বাতিলকরণ"।
কুক্সপ্লসোন

আপনার উত্তর দেওয়ার জন্য ধন্যবাদ. আমি যখন একটি মিনিট খুঁজে পাই তখন আমি অবশ্যই আলোচনার দিকে নজর রাখব।
কনরাড রুডল্ফ

1
ভাল 2017 সম্পাদনা। স্বীকৃত হিসাবে একই, তবে একটি স্কোপ ব্যতিক্রম পয়েন্টার সহ। আমি এটি শীর্ষে রাখব এবং সম্ভবত বাকীগুলি থেকে মুক্তি পাব।
নাথান কুপার

6

আপনার সমস্যাটি হ'ল আপনি একাধিক থ্রেড থেকে একাধিক ব্যতিক্রম পেতে পারেন, কারণ প্রতিটি ব্যর্থ হতে পারে, সম্ভবত বিভিন্ন কারণে।

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

সহজ সমাধান

সহজ সমাধান হ'ল প্রতিটি থ্রেডের সমস্ত ব্যতিক্রম ধরা, ভাগ করে নেওয়া ভেরিয়েবেলে (মূল থ্রেডে) রেকর্ড করা।

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

জটিল সমাধান

আরও জটিল সমাধান হ'ল আপনার প্রতিটি থ্রেডের প্রয়োগের কৌশলগত পয়েন্টগুলি পরীক্ষা করা উচিত, যদি কোনও ব্যতিক্রম অন্য থ্রেড থেকে ফেলে দেওয়া হয়।

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

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

যখন সমস্ত থ্রেড বাতিল হয়ে যায়, মূল থ্রেডটি প্রয়োজনীয়তা ব্যতীত পরিচালনা করতে পারে।


4

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

try
{
  start thread();
  wait_finish( thread );
}
catch(...)
{
  // will catch exceptions generated within start and wait, 
  // but not from the thread itself
}

আপনার প্রয়োজন হতে পারে এমন কোনও ব্যতিক্রম পুনরায় ছুঁড়ে ফেলার জন্য আপনাকে প্রতিটি থ্রেডের মধ্যেই ব্যতিক্রমগুলি ধরতে হবে এবং মূল থ্রেড থেকে প্রস্থান স্থিতির ব্যাখ্যা করতে হবে interpret

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


3

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


উভয় থ্রেড একই প্রক্রিয়াতে থাকলে কেন এটির সিরিয়ালাইজ করা দরকার?
নওয়াজ

1
@ নাওয়াজ কারণ ব্যতিক্রমটিতে থ্রেড-লোকাল ভেরিয়েবলগুলির উল্লেখ রয়েছে যা অন্য থ্রেডে স্বয়ংক্রিয়ভাবে উপলব্ধ হয় না।
tvanfosson

2

এক থ্রেড থেকে অন্য থ্রেডে ব্যতিক্রম প্রেরণ করার জন্য প্রকৃতপক্ষে কোনও ভাল এবং জেনেরিক উপায় নেই।

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

যদি আপনার সমস্ত ব্যতিক্রম স্ট্যান্ডার্ড :: ব্যতিক্রমের উত্তরাধিকারী না হয় তবে আপনি সমস্যায় পড়েছেন এবং আপনার থ্রেডে প্রচুর শীর্ষ স্তরের ক্যাচ লিখতে হবে ... তবে সমাধানটি এখনও ধরে আছে।


1

শ্রমিকের সমস্ত ব্যাতিক্রমের জন্য আপনাকে জেনেরিক ক্যাচ করাতে হবে (অ্যাক্সেস লঙ্ঘনের মতো অ্যাক্সেস লঙ্ঘন সহ), এবং কর্মী থ্রেড থেকে একটি বার্তা প্রেরণ করুন (আমি মনে করি যে আপনার কাছে কোনও প্রকার মেসেজিং রয়েছে?) নিয়ন্ত্রণকারীকে থ্রেড, ব্যতিক্রমটির জন্য সরাসরি পয়েন্টারযুক্ত এবং ব্যতিক্রমের একটি অনুলিপি তৈরি করে সেখানে পুনরায় পাঠাবেন। তারপরে শ্রমিক আসল অবজেক্টটি মুক্ত করে প্রস্থান করতে পারবেন।


1

Http://www.boost.org/doc/libs/release/libs/exception/doc/tutorial_exception_ptr.html দেখুন । শিশু থ্রেডে যোগ দেওয়ার জন্য আপনি যে ফাংশনটি বলছেন তার একটি মোড়ক ফাংশন লেখা সম্ভব, যা স্বয়ংক্রিয়ভাবে শিশু থ্রেড দ্বারা নির্গত কোনও ব্যতিক্রম পুনরায় নিক্ষেপ করে (বুস্ট :: রিথ্রো এক্সসেপশন ব্যবহার করে) exception

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