থ্রেড_লোকাল সি ++ 11 এর অর্থ কী?


131

আমি thread_localসি ++ 11 এর বর্ণনায় বিভ্রান্ত হয়ে পড়েছি । আমার বোধগম্যতা হল, প্রতিটি থ্রেডে একটি ফাংশনে স্থানীয় ভেরিয়েবলের অনন্য অনুলিপি রয়েছে। গ্লোবাল / স্ট্যাটিক ভেরিয়েবলগুলি সমস্ত থ্রেড (সম্ভবত লকগুলি ব্যবহার করে সিঙ্ক্রোনাইজড অ্যাক্সেস) দ্বারা অ্যাক্সেস করা যায়। এবং thread_localভেরিয়েবলগুলি সমস্ত থ্রেডের কাছে দৃশ্যমান তবে কেবল যে থ্রেডের জন্য সেগুলি সংজ্ঞায়িত করা হয়েছে কেবল তা দ্বারা সংশোধন করা যাবে? এটা কি ঠিক?

উত্তর:


151

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

এটি বর্তমান স্বয়ংক্রিয় (ব্লক / ফাংশন চলাকালীন বিদ্যমান), স্ট্যাটিক (প্রোগ্রামের সময়কালের জন্য বিদ্যমান) এবং গতিশীল (বরাদ্দ এবং অবনতির মধ্যে স্তূপে বিদ্যমান) যোগ করে।

থ্রেড-লোকাল এমন কিছু থ্রেড তৈরির সময় অস্তিত্ব নিয়ে আসে এবং থ্রেড বন্ধ হয়ে গেলে নিষ্পত্তি হয়।

কিছু উদাহরণ অনুসরণ।

এলোমেলো সংখ্যার জেনারেটরের কথা চিন্তা করুন যেখানে প্রতি থ্রেড ভিত্তিতে বীজ বজায় রাখতে হবে। একটি থ্রেড-স্থানীয় বীজ ব্যবহার করার অর্থ হ'ল প্রতিটি থ্রেড তার নিজস্ব এলোমেলো সংখ্যা ক্রম পায়, অন্য থ্রেডের থেকে পৃথক।

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

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

এই দুটি উদাহরণই থ্রেড স্থানীয় ভেরিয়েবলটিকে এটির কার্যকারিতাটির মধ্যে বিদ্যমান থাকতে দেয়। প্রাক-থ্রেড কোডে এটি ফাংশনটির মধ্যে কেবল স্ট্যাটিক স্টোরেজ সময়কাল হয়। থ্রেডগুলির জন্য, এটি স্থানীয় স্টোরেজ সময়কালকে থ্রেডে পরিবর্তিত করা হয়েছে।

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

এই সাইটের বিভিন্ন স্টোরেজ সময়কাল নির্দিষ্টকারীগুলির একটি যুক্তিসঙ্গত বিবরণ রয়েছে।


4
থ্রেড স্থানীয় ব্যবহার করে সমস্যাগুলি সমাধান হয় না strtokstrtokএমনকি একক থ্রেডযুক্ত পরিবেশে নষ্ট হয়ে গেছে।
জেমস কানজে

11
দুঃখিত, আমাকে এটি পুনরায় বলি দিন। এটি strtok :
paxdiablo

7
আসলে, এর rঅর্থ দাঁড়ায় "পুনরায় প্রবেশকারী", যার থ্রেড সুরক্ষার সাথে কোনও সম্পর্ক নেই। এটি সত্য যে আপনি থ্রেড-লোকাল স্টোরেজ দিয়ে কিছু জিনিস সুরক্ষিতভাবে থ্রেড-কাজ করতে পারবেন তবে আপনি সেগুলি পুনরায় প্রবেশ করতে পারবেন না।
কেরেক এসবি

5
একক থ্রেডযুক্ত পরিবেশে, কলগুলি গ্রাফের কোনও চক্রের অংশ হলে কেবল ফাংশনগুলিকে পুনরায় প্রবেশ করা দরকার। একটি পাতার ফাংশন (এটি যা অন্য ফাংশনগুলিকে কল করে না) সংজ্ঞা অনুসারে একটি চক্রের অংশ নয়, এবং strtokঅন্যান্য ক্রিয়াকলাপগুলি কল করার কোনও কারণ নেই ।
এমসাল্টার

3
এটি এতে বিশৃঙ্খলা সৃষ্টি করবে: while (something) { char *next = strtok(whatever); someFunction(next); // someFunction calls strtok }
18:18

135

আপনি যখন ভেরিয়েবল ঘোষণা করেন thread_localতখন প্রতিটি থ্রেডের নিজস্ব অনুলিপি থাকে। আপনি যখন নাম দ্বারা এটি উল্লেখ করেন, তারপরে বর্তমান থ্রেডের সাথে সম্পর্কিত অনুলিপিটি ব্যবহৃত হয়। যেমন

thread_local int i=0;

void f(int newval){
    i=newval;
}

void g(){
    std::cout<<i;
}

void threadfunc(int id){
    f(id);
    ++i;
    g();
}

int main(){
    i=9;
    std::thread t1(threadfunc,1);
    std::thread t2(threadfunc,2);
    std::thread t3(threadfunc,3);

    t1.join();
    t2.join();
    t3.join();
    std::cout<<i<<std::endl;
}

এই কোডটি "2349", "3249", "4239", "4329", "2439" বা "3429" আউটপুট দেবে, তবে আর কিছুই নয়। প্রতিটি থ্রেডের নিজস্ব অনুলিপি থাকে i, যা বরাদ্দ করা হয়, বর্ধিত হয় এবং তারপরে মুদ্রিত হয়। থ্রেড চলমানটির mainনিজস্ব অনুলিপিও রয়েছে, যা শুরুতে বরাদ্দ করা হয় এবং তারপরে অপরিবর্তিত থাকে। এই অনুলিপিগুলি সম্পূর্ণ স্বাধীন, এবং প্রত্যেকের আলাদা আলাদা ঠিকানা রয়েছে।

এটি কেবলমাত্র সেই নামটিই সেই ক্ষেত্রে বিশেষ --- --- যদি আপনি কোনও thread_localভেরিয়েবলের ঠিকানা নেন তবে আপনার কেবল একটি সাধারণ পয়েন্টারের কাছে একটি সাধারণ পয়েন্টার রয়েছে যা আপনি অবাধে থ্রেডের মধ্যে দিয়ে যেতে পারেন। যেমন

thread_local int i=0;

void thread_func(int*p){
    *p=42;
}

int main(){
    i=9;
    std::thread t(thread_func,&i);
    t.join();
    std::cout<<i<<std::endl;
}

যেহেতু ঠিকানাটি iথ্রেড ফাংশনে প্রেরণ করা হয়েছে, তারপরে iমূল থ্রেডের অন্তর্ভুক্ত থাকা অনুলিপিটি যদিও তা অর্পণ করা যেতে পারে thread_local। এই প্রোগ্রামটি এভাবে "42" আউটপুট দেয়। যদি আপনি এটি করেন, তবে আপনার যত্ন নেওয়া দরকার যে *pথ্রেডটি তার নিজস্ব হওয়া থ্রেডটি বের হয়ে যাওয়ার পরে অ্যাক্সেস করা হয়নি, অন্যথায় আপনি পয়েন্ট-টু অবজেক্টটি নষ্ট হয়ে যাওয়ার মতো অন্যান্য ক্ষেত্রে যেমন ঝুঁকির পয়েন্টার এবং অপরিজ্ঞাত আচরণ পান।

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

struct my_class{
    my_class(){
        std::cout<<"hello";
    }
    ~my_class(){
        std::cout<<"goodbye";
    }
};

void f(){
    thread_local my_class unused;
}

void do_nothing(){}

int main(){
    std::thread t1(do_nothing);
    t1.join();
}

এই প্রোগ্রামটিতে 2 টি থ্রেড রয়েছে: মূল থ্রেড এবং ম্যানুয়ালি তৈরি থ্রেড। কোনও থ্রেড কল করে না f, তাই thread_localবস্তুটি কখনই ব্যবহৃত হয় না। সুতরাং এটি নির্ধারিত নয় যে সংকলক 0, 1 বা 2 my_classটির উদাহরণ তৈরি করে কিনা এবং আউটপুট "", "হ্যালোহেলোগুডবিগুডবি" বা "হেলোগুডবি" হতে পারে।


1
আমি মনে করি এটি উল্লেখ করা গুরুত্বপূর্ণ যে ভ্যারিয়েবলের থ্রেড-স্থানীয় অনুলিপিটি ভেরিয়েবলের একটি নতুন প্রারম্ভিক অনুলিপি। মানে, যদি আপনি একটি যোগ g()শুরুতে কল threadFunc, তারপর আউটপুট হবে 0304029বা যুগলের কিছু অন্যান্য বিন্যাস 02, 03এবং 04। অর্থাৎ 9 iথ্রেড তৈরির আগে নির্ধারিত হলেও থ্রেডগুলি iযেখানে নতুনভাবে নির্মিত কপি পায় i=0। যদি এর iসাথে নির্ধারিত হয় thread_local int i = random_integer(), তবে প্রতিটি থ্রেড একটি নতুন এলোমেলো পূর্ণসংখ্যা পায়।
মার্ক এইচ

ঠিক করা হয় নি একটি বিন্যাস 02, 03, 04, সেখানে মত অন্যান্য সিকোয়েন্স হতে পারে020043
Hongxu চেন

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

23

থ্রেড-লোকাল স্টোরেজ স্ট্যাটিক (= গ্লোবাল) স্টোরেজের মতো প্রতিটি দিকেই রয়েছে কেবলমাত্র প্রতিটি থ্রেডে অবজেক্টের আলাদা কপি রয়েছে। অবজেক্টের লাইফ টাইম হয় থ্রেড স্টার্ট (গ্লোবাল ভেরিয়েবলের জন্য) বা প্রথম ইনিশিয়েশন (ব্লক-লোকাল স্ট্যাটিক্সের জন্য) এ শুরু হয় এবং থ্রেড শেষ হয়ে গেলে (অর্থাৎ যখন join()ডাকা হয়) শেষ হয়।

ফলস্বরূপ, কেবলমাত্র ভেরিয়েবলগুলিও ঘোষিত staticহতে পারে thread_localযেমন গ্লোবাল ভেরিয়েবলগুলি (আরও স্পষ্টভাবে: ভেরিয়েবলগুলি "নেমস্পেস স্কোপ এ"), স্ট্যাটিক শ্রেণীর সদস্য এবং ব্লক-স্ট্যাটিক ভেরিয়েবল (যার ক্ষেত্রে staticবোঝানো হয়)।

উদাহরণস্বরূপ, ধরুন আপনার কাছে একটি থ্রেড পুল রয়েছে এবং আপনার কাজের বোঝা কতটা ভারসাম্যপূর্ণ হচ্ছে তা জানতে চান:

thread_local Counter c;

void do_work()
{
    c.increment();
    // ...
}

int main()
{
    std::thread t(do_work);   // your thread-pool would go here
    t.join();
}

এটি থ্রেড ব্যবহারের পরিসংখ্যান মুদ্রণ করবে, উদাহরণস্বরূপ এর মতো একটি বাস্তবায়ন সহ:

struct Counter
{
     unsigned int c = 0;
     void increment() { ++c; }
     ~Counter()
     {
         std::cout << "Thread #" << std::this_thread::id() << " was called "
                   << c << " times" << std::endl;
     }
};
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.