মূল () প্রস্থান করার সময় একটি বিচ্ছিন্ন থ্রেডের কী হবে?


152

ধরে নিন আমি একটি std::threadএবং তারপরে detach()এটি শুরু করছি , সুতরাং থ্রেডটি চালানো অব্যাহত রাখে যদিও এটি std::threadএকবার এটি উপস্থাপন করে, সুযোগের বাইরে চলে যায়।

আরও ধরে নিন যে প্রোগ্রামটির বিচ্ছিন্ন থ্রেড 1 এ যোগদানের জন্য একটি নির্ভরযোগ্য প্রোটোকল নেই , সুতরাং বিচ্ছিন্ন থ্রেডটি main()প্রস্থান করার পরে এখনও চলে ।

আমি স্ট্যান্ডার্ডে কিছু খুঁজে পাচ্ছি না (আরও স্পষ্টভাবে, N3797 C ++ 14 খসড়াতে), যা ঘটতে হবে তা বর্ণনা করে, 1.10 বা 30.3 এর মধ্যে প্রাসঙ্গিক শব্দটি নেই।

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

যদি main()পৃথক থ্রেডের সাথে চলমান চলমান অপরিজ্ঞাত আচরণ হয়, তবে মূল থ্রেডটি কখনই 2 থেকে বের না হওয়া অবধি কোনও ব্যবহার std::thread::detach()অপরিজ্ঞাত আচরণ ।

সুতরাং, চলমান main()বিচ্ছিন্ন থ্রেডগুলির সাথে দৌড়াতে অবশ্যই সংজ্ঞাযুক্ত প্রভাব থাকতে হবে। প্রশ্নটি হল: যেখানে ( সি ++ স্ট্যান্ডার্ডে , পসিক্স নয়, ওএস ডক্স নয়, ...) সেগুলি প্রভাবগুলি সংজ্ঞায়িত করা হয়েছে।

2 একটি বিচ্ছিন্ন থ্রেড যোগদান করা যাবে না (অর্থে std::thread::join())। আপনি পৃথক থ্রেড থেকে ফলাফলের জন্য অপেক্ষা করতে পারেন (যেমন ভবিষ্যতের মাধ্যমে std::packaged_task, অথবা একটি গণনা সেমফোর বা পতাকা এবং শর্ত ভেরিয়েবলের মাধ্যমে), তবে এটি গ্যারান্টি দেয় না যে থ্রেডটি কার্যকর করা শেষ হয়েছে । বস্তুত, যদি না আপনি থ্রেড প্রথম স্বয়ংক্রিয় বস্তুর বিনাশকারী মধ্যে সংকেত অংশ করা, সেখানে হবে , সাধারণভাবে, কোড (destructors) যে চলবে পর সংকেত কোড। ওএস ফলাফলটি গ্রাহ্য করার জন্য মূল থ্রেডকে শিডিউল করে এবং বিচ্ছিন্ন থ্রেডটি শেষ হওয়ার আগে প্রস্থানকারীরা বলেন, কী ঘটবে?


5
আমি কেবলমাত্র [বেসিক.স্টার্ট.মিটার] / 4 এ খুব অস্পষ্ট নন-বাধ্যতামূলক নোটটি খুঁজে পেতে পারি: " এই প্রয়োজনীয়তাগুলি পূরণ করার জন্য কল করার আগে std::exitবা প্রস্থান করার আগে প্রতিটি থ্রেড সমাপ্ত mainকরা যথেষ্ট তবে প্রয়োজনীয় নয়" " (পুরো অনুচ্ছেদটি প্রাসঙ্গিক হতে পারে) এছাড়াও দেখুন [সমর্থন. std::exitmain
start.term

উত্তর:


45

মূল প্রশ্নের "" main()প্রস্থান করার পরে একটি বিচ্ছিন্ন থ্রেডের সাথে কী ঘটে "এর উত্তরটি হ'ল:

এটি চলতে থাকে (কারণ এটি স্ট্যান্ডার্ড বলে না যে এটি বন্ধ হয়ে গেছে), এবং এটি যথাযথভাবে সংজ্ঞায়িত করা হয়, যতক্ষণ না এটি অন্য থ্রেড বা স্থিতিক বস্তুর ভেরিয়েবল (স্বয়ংক্রিয় | থ্রেড_লোকাল) না স্পর্শ করে।

এটি থ্রেড ম্যানেজারকে স্ট্যাটিক অবজেক্ট হিসাবে মঞ্জুরি দেওয়ার মঞ্জুরিপ্রাপ্ত বলে মনে হয় (নোট [মূল.start.term এ নোট ] / 4 যতটা বলেছেন, পয়েন্টারের জন্য @dyp ধন্যবাদ))

সমস্যা, উঠা যখন স্ট্যাটিক বস্তুর ধ্বংস সমাপ্ত হয়েছে কারণ তারপর ফাঁসি সরকার যেখানে শুধুমাত্র কোড সংকেত হ্যান্ডলার অনুমতিপ্রাপ্ত চালানো হতে পারে (প্রবেশ [basic.start.term] / 1, 1 ম বাক্য )। সি ++ স্ট্যান্ডার্ড লাইব্রেরির মধ্যে কেবল এটি <atomic>গ্রন্থাগার ( [সমর্থন.আরুনটাইম] / 9, দ্বিতীয় বাক্য )। বিশেষত, — সাধারণভাবে — এটি বাদ দেয় condition_variable (এটি বাস্তবায়ন-সংজ্ঞায়িত হয় যে এটি কোনও সিগন্যাল হ্যান্ডলারে ব্যবহারের জন্য সংরক্ষণ করা উচিত, কারণ এটি অংশ নয় <atomic>)।

আপনি যদি এই মুহুর্তে আপনার স্ট্যাকটি আনবাউন্ড না করেন তবে কীভাবে অপরিজ্ঞাত আচরণ এড়ানো যায় তা দেখা শক্ত।

দ্বিতীয় প্রশ্নের উত্তর "বিচ্ছিন্ন থ্রেডগুলি আবার কখনও যুক্ত হতে পারে":

হ্যাঁ, সঙ্গে *_at_thread_exitফাংশন পরিবার ( notify_all_at_thread_exit(), std::promise::set_value_at_thread_exit(), ...)।

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

সিগন্যালিং শেষ জিনিস থ্রেড মতো (দৌড়ানো পর স্বয়ংক্রিয় এবং থ্রেড-স্থানীয় বস্তুর destructors করেছে-ঘটেছিল ) কি _at_thread_exitফাংশন পরিবারের জন্য পরিকল্পনা করা হয়েছিল।

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


17
আপনি যদি এই বিষয়ে নিশ্চিত? আমি যেখানেই পরীক্ষা করেছি (জিসিসি 5, ঝাঁকুনি 3.5, এমএসভিসি 14), মূল থ্রেডটি প্রস্থান করলে সমস্ত বিচ্ছিন্ন থ্রেড মারা যায়।
rustx

3
আমি বিশ্বাস করি যে সমস্যাটি একটি নির্দিষ্ট বাস্তবায়ন যা করে তা নয়, তবে মানটি কীভাবে নির্ধারিত আচরণ হিসাবে সংজ্ঞা দেয় তা এড়াতে হবে।
জন স্পেন্সার

7
এই উত্তরটি বোঝা যাচ্ছে যে স্ট্যাটিক ভেরিয়েবলগুলি ধ্বংস করার পরে প্রক্রিয়াটি কোনও ধরণের ঘুমের রাজ্যে চলে যাবে অন্য কোনও থ্রেড শেষ হওয়ার অপেক্ষায়। এটি সত্য নয়, exitস্ট্যাটিক অবজেক্টগুলি, চলমান atexitহ্যান্ডলারগুলি, ফ্লাশিং স্ট্রিম ইত্যাদি ধ্বংস করার পরে এটি হোস্ট পরিবেশের নিয়ন্ত্রণ ফিরে আসে, অর্থাৎ প্রক্রিয়াটি প্রস্থান করে। যদি কোনও বিচ্ছিন্ন থ্রেড এখনও চলমান থাকে (এবং এটির নিজের থ্রেডের বাইরে কোনও কিছু স্পর্শ না করে কোনওরকম সংজ্ঞায়িত আচরণ এড়িয়ে গেছে) তবে প্রক্রিয়াটি বেরিয়ে আসার সাথে সাথে এটি ধূমপানের ধোঁয়ায় অদৃশ্য হয়ে যায়।
জোনাথন ওয়েকেলি

3
আপনি যদি নন-আইএসও সি ++ এপিআই ব্যবহার করে ঠিকঠাক থাকেন তবে যদি ফিরে আসার বা mainকল করার pthread_exitপরিবর্তে কলগুলি হয় exitতবে এর ফলে প্রক্রিয়াটি পৃথক থ্রেডগুলি শেষ exitহওয়ার জন্য অপেক্ষা করবে এবং তারপরে শেষটি শেষ হওয়ার পরে কল করবে।
জোনাথন ওয়েকেলি

3
"এটি চলতে থাকে (কারণ স্ট্যান্ডার্ড এটি থামিয়েছে বলে বলে না)" -> কেউ আমাকে কীভাবে বলতে পারেন যে কোনও থ্রেড তার ধারক প্রক্রিয়াটি চালিয়ে যেতে পারে?
গুপ্ত

42

থ্রেড বিচ্ছিন্ন করা

মতে std::thread::detach:

মৃত্যুদন্ড কার্যকরভাবে চালিয়ে যাওয়ার অনুমতি দিয়ে থ্রেড অবজেক্ট থেকে এক্সিকিউশনের থ্রেড পৃথক করে। থ্রেডটি বের হয়ে গেলে যে কোনও বরাদ্দকৃত সম্পদ মুক্ত করা হবে।

থেকে pthread_detach:

Pthread_detach () ফাংশনটি প্রয়োগের দিকে ইঙ্গিত দেয় যে থ্রেডটি বন্ধ হয়ে গেলে থ্রেডের জন্য স্টোরেজটি পুনরায় দাবি করা যেতে পারে। যদি থ্রেডটি বন্ধ না করা হয়, pthread_detach () এটি বন্ধ করার কারণ হবে না। একই টার্গেট থ্রেডে একাধিক pthread_detach () কলগুলির প্রভাব অনির্দিষ্ট।

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

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

থ্রেডস হত্যা

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

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

থ্রেড সমর্থন

থ্রেডগুলি সমর্থিত না হলে একটি সরল প্রক্রিয়া রয়েছে বলে একটি std::thread::get_id()অবৈধ আইডি (পূর্বনির্ধারিত নির্মিত std::thread::id) ফেরত দেওয়া উচিত , যার জন্য চালনার জন্য থ্রেড অবজেক্টের প্রয়োজন হয় না এবং একটি নির্মাণকারীকে std::threadএকটি নিক্ষেপ করা উচিত std::system_error। আজকের ওএসের সাথে একত্রে আমি সি ++ 11 টি বুঝতে পারি। যদি থ্রেডিং সমর্থন সহ কোনও ওএস থাকে, যা এর প্রক্রিয়াগুলিতে একটি প্রধান থ্রেড উত্সাহিত করে না, আমাকে জানান।

থ্রেড নিয়ন্ত্রণ করছে

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


3
যেহেতু প্রতিটি থ্রেডের নিজস্ব স্ট্যাক রয়েছে (যা লিনাক্সে মেগাবাইট প্রস্থে রয়েছে) তাই আমি থ্রেডটি আলাদা করতে বেছে নেব (সুতরাং এটির স্ট্যাকটি প্রস্থান করার সাথে সাথেই তা মুক্ত করা হবে) এবং মূল থ্রেডটি প্রস্থান করতে গেলে কিছু সিঙ্ক আদিম ব্যবহার করা হবে (এবং সঠিক শাটডাউন করার জন্য এটি প্রত্যাবর্তন / প্রস্থানের সময় বন্ধ করার পরিবর্তে চলমান থ্রেডগুলিতে যোগ হওয়া দরকার)।
নরবার্ট বারকি

8
আমি আসলে দেখতে পাচ্ছি না কীভাবে এটি প্রশ্নের উত্তর দেয়
মাইকেএমবি

18

নিম্নলিখিত কোড বিবেচনা করুন:

#include <iostream>
#include <string>
#include <thread>
#include <chrono>

void thread_fn() {
  std::this_thread::sleep_for (std::chrono::seconds(1)); 
  std::cout << "Inside thread function\n";   
}

int main()
{
    std::thread t1(thread_fn);
    t1.detach();

    return 0; 
}

এটি একটি লিনাক্স সিস্টেমে চালিত হয়ে থ্রেড_ফএন-এর বার্তাটি কখনও মুদ্রিত হয় না। বাহিরের বাইরে বেরোনোর thread_fn()সাথে সাথেই ওএস সত্যই পরিষ্কার হয়ে main()যায়। প্রতিস্থাপন করা হচ্ছে t1.detach()সঙ্গে t1.join()সবসময় বার্তা আশানুরূপ ছাপে।


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

17

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

কোনওটিকে আলাদা করার সময় std::thread, এই তিনটি শর্তটি ধরে রাখা উচিত:

  1. *this আর কোনও থ্রেডের মালিক নেই
  2. joinable() সর্বদা সমান হবে false
  3. get_id() সমান হবে std::thread::id()

1
অপরিচ্ছন্ন কেন? কারণ মান কিছু সংজ্ঞায়িত করে না? আমার পাদটীকা দ্বারা, যে কোনও কলকে detach()অপরিজ্ঞিত আচরণ করতে দেবে না ? বিশ্বাস করা শক্ত ...
মার্ক মুটজ - মিমুতজ

2
@ মার্কমুটজ-মিমুটজ এই অর্থেই সংজ্ঞায়িত যে প্রক্রিয়াটি যদি প্রস্থান হয় তবে থ্রেডের ভাগ্য অপরিজ্ঞাত।
সিজার

2
@ কায়সার এবং কীভাবে আমি থ্রেড শেষ হওয়ার আগেই প্রস্থান না করা নিশ্চিত করব?
মিশাল এইচআর

6

যখন মূল থ্রেড (অর্থাৎ, মূল () ফাংশনটি চালিত থ্রেডটি শেষ হয়) তখন প্রক্রিয়াটি সমাপ্ত হয় এবং অন্যান্য সমস্ত থ্রেড বন্ধ হয়ে যায়।

রেফারেন্স: https://stackoverflow.com/a/4667273/2194843


0

অন্যান্য থ্রেডগুলি কার্যকরভাবে চালিয়ে যাওয়ার অনুমতি দিতে, মূল থ্রেডটি প্রস্থান (3) এর পরিবর্তে pthread_exit () কল করে শেষ করা উচিত। মূলত pthread_exit ব্যবহার করা ভাল। যখন pthread_exit ব্যবহার করা হয়, তখন মূল থ্রেডটি কার্যকর করা বন্ধ হয়ে যায় এবং অন্যান্য থ্রেডগুলি প্রস্থান না হওয়া অবধি জম্বি (বিযুক্ত) স্থিতিতে থাকবে। যদি আপনি মূল থ্রেডে pthread_exit ব্যবহার করেন, অন্য থ্রেডগুলির রিটার্নের স্থিতি পেতে পারেন না এবং অন্যান্য থ্রেডগুলির জন্য ক্লিন-আপ করতে না পারেন (pthread_join (3) ব্যবহার করে করা যেতে পারে)। এছাড়াও, থ্রেডগুলি পৃথক করা ভাল (pthread_detach (3)) যাতে থ্রেড সংস্থানগুলি স্বয়ংক্রিয়ভাবে থ্রেড সমাপ্তিতে প্রকাশিত হয়। সমস্ত থ্রেড প্রস্থান না করা পর্যন্ত ভাগ করা সংস্থানগুলি প্রকাশ করা হবে না।


@ কেজিভিনড, কেন "pthread_exit (0)" যুক্ত করবেন না; "ti.detach ()" এর পরে;
ইশি

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