1. নিরাপদে সংজ্ঞায়িত কীভাবে ?
শব্দার্থিক ভাবে। এই ক্ষেত্রে, এটি কোনও কঠোর সংজ্ঞাযুক্ত শব্দ নয়। এর অর্থ কেবল "ঝুঁকি ছাড়াই আপনি এটি করতে পারেন"।
২. যদি কোনও প্রোগ্রাম একযোগে নিরাপদে সম্পাদন করা যায় তবে এর অর্থ কি সর্বদা এটি পুনরায় প্রেরণকারী?
না।
উদাহরণস্বরূপ, আসুন একটি সি ++ ফাংশন থাকুন যা প্যারামিটার হিসাবে লক এবং কলব্যাক উভয়ই গ্রহণ করে:
#include <mutex>
typedef void (*callback)();
std::mutex m;
void foo(callback f)
{
m.lock();
// use the resource protected by the mutex
if (f) {
f();
}
// use the resource protected by the mutex
m.unlock();
}
অন্য ফাংশনটির জন্য একই মুটেক্সটিকে লক করার দরকার হতে পারে:
void bar()
{
foo(nullptr);
}
প্রথম দর্শনে, সবকিছু ঠিক আছে বলে মনে হচ্ছে ... তবে অপেক্ষা করুন:
int main()
{
foo(bar);
return 0;
}
যদি মিটেক্সে থাকা লকটি পুনরাবৃত্তি না হয় তবে মূল থ্রেডে এখানে কী ঘটবে তা এখানে:
main
ডাকবে foo
।
foo
লক অর্জন করবে
foo
কল করবে bar
, যা ডাকবে foo
।
- দ্বিতীয়টি
foo
লকটি অর্জন করার চেষ্টা করবে, ব্যর্থ হবে এবং এটি প্রকাশের জন্য অপেক্ষা করবে।
- অচলাবস্থা।
- ওহো ...
ঠিক আছে, আমি কলব্যাক জিনিসটি ব্যবহার করে প্রতারণা করেছি। তবে কোডের আরও জটিল টুকরোগুলি একইরকম প্রভাব ফেলেছে তা কল্পনা করা সহজ।
৩. পুনরায় পাঠকের ক্ষমতার জন্য আমার কোডটি পরীক্ষা করার সময় আমার মনে রাখা উচিত যে উল্লিখিত ছয়টি দফার মধ্যে সাধারণ থ্রেডটি ঠিক কী?
আপনি করতে পারেন গন্ধ একটি সমস্যা আপনার ফাংশন আছে / একটি সংশোধনযোগ্য ক্রমাগত সম্পদ অ্যাক্সেস দেয়, অথবা হয়েছে / একটি ফাংশন যা একসেস দেয় গন্ধ পাচ্ছি ।
( ঠিক আছে, আমাদের কোডের 99% গন্ধ হওয়া উচিত, তারপরে ... এটি হ্যান্ডেল করার জন্য শেষ বিভাগটি দেখুন ... )
সুতরাং, আপনার কোড অধ্যয়ন করে, সেই পয়েন্টগুলির মধ্যে একটি আপনাকে সতর্ক করা উচিত:
- ফাংশনটির একটি রাষ্ট্র রয়েছে (যেমন একটি গ্লোবাল ভেরিয়েবল অ্যাক্সেস করুন, এমনকি কোনও শ্রেণীর সদস্যের ভেরিয়েবল)
- এই ফাংশনটি একাধিক থ্রেড দ্বারা ডাকা যেতে পারে, বা প্রক্রিয়াটি সম্পাদন করার সময় স্ট্যাকের মধ্যে দু'বার উপস্থিত হতে পারে (যেমন ফাংশনটি সরাসরি বা অপ্রত্যক্ষভাবে নিজেকে কল করতে পারে)। প্যারামিটার হিসাবে কলব্যাক নেওয়া ফাংশন প্রচুর গন্ধ পায় ।
নন-পুনঃপ্রবর্তনটি ভাইরাল: মনে রাখবেন যে এমন একটি ফাংশন যা কোনও সম্ভাব্য অ-পুনঃপ্রণালী ফাংশনটিকে কল করতে পারে তাকে পুনরায় তদন্তকারী হিসাবে বিবেচনা করা যায় না।
দ্রষ্টব্য, এছাড়াও, যে সি ++ পদ্ধতিগুলির গন্ধ তারা অ্যাক্সেস পেয়েছেthis
, তাই আপনার কোনও মজাদার ইন্টারঅ্যাকশন না রয়েছে তা নিশ্চিত করার জন্য আপনার কোডটি অধ্যয়ন করা উচিত।
4.1। সমস্ত পুনরাবৃত্ত ফাংশন কি প্রতীক্ষিত হয়?
না।
মাল্টিথ্রেডেড ক্ষেত্রে, ভাগ করা সংস্থান অ্যাক্সেস করা একটি পুনরাবৃত্ত ফাংশন একই মুহুর্তে একাধিক থ্রেড দ্বারা কল করা যেতে পারে, ফলস্বরূপ খারাপ / দূষিত ডেটার ফলস্বরূপ।
একক থ্রেডযুক্ত ক্ষেত্রে, একটি পুনরাবৃত্ত ফাংশন একটি পুনঃপ্রেরণকারী ফাংশন (কুখ্যাত strtok
) এর মতো ব্যবহার করতে পারে , বা ডেটা ইতিমধ্যে ব্যবহৃত হয়েছে তা হস্তান্তর না করে বৈশ্বিক ডেটা ব্যবহার করতে পারে। সুতরাং আপনার ফাংশনটি পুনরাবৃত্ত কারণ এটি সরাসরি বা অপ্রত্যক্ষভাবে নিজেকে কল করে তবে এটি পুনরাবৃত্ত-অনিরাপদ হতে পারে ।
4.2। সমস্ত থ্রেড-সেফ ফাংশন কি পুনরায় অনুপ্রবেশকারী?
উপরের উদাহরণে, আমি দেখিয়েছি কীভাবে আপাতভাবে থ্রেডসেফ ফাংশনটি তত্পর হয় নি। ঠিক আছে, আমি কলব্যাক প্যারামিটারের কারণে প্রতারণা করেছি। তবে তারপরে, থ্রেডটিকে অ-পুনরাবৃত্ত লকটি দ্বিগুণ অর্জনের মাধ্যমে অচল করে দেওয়ার একাধিক উপায় রয়েছে।
4.3। সমস্ত পুনরাবৃত্ত এবং থ্রেড-নিরাপদ ফাংশনগুলি কি প্রত্যাবর্তিত হয়?
আমি "হ্যাঁ" বলব যদি "পুনরাবৃত্ত" দ্বারা আপনার অর্থ "পুনরাবৃত্ত-নিরাপদ" হয়।
যদি আপনি গ্যারান্টি দিতে পারেন যে কোনও ফাংশনকে একাধিক থ্রেড দ্বারা এক সাথে ডাকা যেতে পারে এবং সমস্যা ছাড়াই প্রত্যক্ষ বা অপ্রত্যক্ষভাবে নিজেকে কল করতে পারে তবে তা পুনরায় হয়।
সমস্যাটি এই গ্যারান্টিটি মূল্যায়ন করছে ... ^ _ ^ ^
৫. পুনরায় প্রবেশ এবং থ্রেড সুরক্ষার মতো পদগুলি কি একেবারে নিরঙ্কুশ, অর্থাৎ তাদের কি স্থির সংজ্ঞা রয়েছে?
আমি বিশ্বাস করি তারা করে, তবে তারপরে কোনও ফাংশনের মূল্যায়ন করা থ্রেড-সেফ বা পুনরায় প্রবর্তন করা কঠিন হতে পারে। এই কারণেই আমি উপরের গন্ধ শব্দটি ব্যবহার করেছি : আপনি খুঁজে পেতে পারেন যে কোনও ফাংশন পুনরায় প্রেরণকারী নয়, তবে জটিল জটিল কোডের পুনরায় প্রেরণকারী তা নিশ্চিত হওয়া কঠিন হতে পারে
6. একটি উদাহরণ
আসুন ধরে নেওয়া যাক আপনার একটি অবজেক্ট রয়েছে যার একটি পদ্ধতি রয়েছে যার জন্য কোনও সংস্থান ব্যবহার করা দরকার:
struct MyStruct
{
P * p;
void foo()
{
if (this->p == nullptr)
{
this->p = new P();
}
// lots of code, some using this->p
if (this->p != nullptr)
{
delete this->p;
this->p = nullptr;
}
}
};
প্রথম সমস্যাটি হ'ল যদি কোনওভাবে এই ফাংশনটিকে পুনরাবৃত্তভাবে বলা হয় (যেমন এই ফাংশনটি নিজেকে প্রত্যক্ষ বা অপ্রত্যক্ষভাবে কল করে), কোড সম্ভবত ক্রাশ this->p
হবে , কারণ শেষ কলের শেষে মুছে ফেলা হবে, এবং সম্ভবত সম্ভবত শেষ হওয়ার আগে ব্যবহার করা হবে প্রথম কল
সুতরাং, এই কোডটি পুনরাবৃত্ত-নিরাপদ নয় ।
আমরা এটি সংশোধন করতে একটি রেফারেন্স কাউন্টার ব্যবহার করতে পারি:
struct MyStruct
{
size_t c;
P * p;
void foo()
{
if (c == 0)
{
this->p = new P();
}
++c;
// lots of code, some using this->p
--c;
if (c == 0)
{
delete this->p;
this->p = nullptr;
}
}
};
এইভাবে কোডটি পুনরাবৃত্ত-নিরাপদে পরিণত হয় ... তবে মাল্টিথ্রেডিং ইস্যুগুলির কারণে এটি এখনও তত্পর হয় না: আমাদের অবশ্যই নিশ্চিত হতে হবে যে একটি পুনরাবৃত্তিকারী মুটেক্স ব্যবহার করে c
এবং এর সংশোধনগুলি পরমাণুভাবে করা হবে (সমস্ত মিউটেক্স পুনরাবৃত্ত নয়):p
#include <mutex>
struct MyStruct
{
std::recursive_mutex m;
size_t c;
P * p;
void foo()
{
m.lock();
if (c == 0)
{
this->p = new P();
}
++c;
m.unlock();
// lots of code, some using this->p
m.lock();
--c;
if (c == 0)
{
delete this->p;
this->p = nullptr;
}
m.unlock();
}
};
এবং অবশ্যই, এই সব অনুমান lots of code
নিজেই ব্যবহার সমেত পুন: প্রবেশ হয় p
।
এবং উপরের কোডটি দূর থেকেও ব্যতিক্রম-নিরাপদ নয় , তবে এটি অন্য গল্প… ^ _ ^ ^
Hey. আরে আমাদের কোডের ৯৯% আবার তদন্তকারী নয়!
স্প্যাগেটি কোডের জন্য এটি বেশ সত্য। তবে আপনি যদি নিজের কোডটি সঠিকভাবে বিভাজন করেন তবে আপনি পুনরায় সমস্যা এড়াতে পারবেন।
7.1। নিশ্চিত করুন যে সমস্ত ক্রিয়াকলাপের কোনও রাজ্য নেই
তাদের কেবলমাত্র প্যারামিটারগুলি, তাদের নিজস্ব স্থানীয় ভেরিয়েবলগুলি, রাষ্ট্র ব্যতীত অন্যান্য ফাংশনগুলি এবং ডেটা অনুলিপিগুলি যদি কিছুটা ফিরে আসে তবে তাদের অবশ্যই ব্যবহার করতে হবে।
7.2। আপনার অবজেক্টটি "পুনরাবৃত্ত-নিরাপদ" কিনা তা নিশ্চিত করুন
একটি অবজেক্ট পদ্ধতিতে অ্যাক্সেস রয়েছে this
, সুতরাং এটি অবজেক্টের একই উদাহরণের সমস্ত পদ্ধতির সাথে একটি স্টেট ভাগ করে।
সুতরাং, নিশ্চিত হয়ে নিন যে বস্তুটি স্ট্যাকের এক পর্যায়ে (যেমন কল করার পদ্ধতি এ) ব্যবহার করা যেতে পারে, এবং তারপরে, অন্য কোনও পয়েন্টে (অর্থাত্ বি কল করার পদ্ধতিটি) পুরো অবজেক্টকে কলুষিত না করেই ব্যবহার করা যেতে পারে। আপনার অবজেক্টটি এমনভাবে তৈরি করুন যে কোনও পদ্ধতিটি বেরিয়ে যাওয়ার পরে অবজেক্টটি স্থিতিশীল এবং সঠিক (কোনও ঝুঁকির পয়েন্টার নেই, সদস্যের বৈকল্পিকের বিরোধী নয় ইত্যাদি)।
7.3। আপনার সমস্ত বস্তু সঠিকভাবে আবদ্ধ হয়েছে তা নিশ্চিত করুন
অন্য কারওও অভ্যন্তরীণ তথ্য অ্যাক্সেস করা উচিত নয়:
// bad
int & MyObject::getCounter()
{
return this->counter;
}
// good
int MyObject::getCounter()
{
return this->counter;
}
// good, too
void MyObject::getCounter(int & p_counter)
{
p_counter = this->counter;
}
এমনকি কনস্টের রেফারেন্সটি ফিরিয়ে দেওয়া বিপজ্জনক হতে পারে যদি ব্যবহারকারী ডেটার ঠিকানাটি পুনরুদ্ধার করে, কারণ কোডের অন্য কোনও অংশ কনস্টের রেফারেন্সটি না বলে কোডটি পরিবর্তন করতে পারে।
7.4। নিশ্চিত করুন যে ব্যবহারকারী জানেন যে আপনার জিনিসটি থ্রেড-নিরাপদ নয়
সুতরাং, ব্যবহারকারী থ্রেডগুলির মধ্যে ভাগ করে নেওয়া কোনও বস্তু ব্যবহারের জন্য মিউটেক্সগুলি ব্যবহার করার জন্য দায়বদ্ধ।
এসটিএল থেকে প্রাপ্ত বস্তুগুলি থ্রেড-নিরাপদ না হওয়ার জন্য ডিজাইন করা হয়েছে (পারফরম্যান্স সমস্যার কারণে), এবং এইভাবে, কোনও ব্যবহারকারী যদি std::string
দুটি থ্রেডের মধ্যে ভাগ করতে চান তবে ব্যবহারকারীর অবশ্যই তার অ্যাক্সেসকে সম্মতিযুক্ত আদিমতার সাথে রক্ষা করতে হবে;
7.5। আপনার থ্রেড-সেফ কোডটি পুনরাবৃত্ত-নিরাপদ রয়েছে তা নিশ্চিত করুন
এর অর্থ পুনরাবৃত্তিমূলক মুটেক্সেস ব্যবহার করা যদি আপনি বিশ্বাস করেন যে একই সূত্রটি একই থ্রেড দ্বারা দু'বার ব্যবহার করা যেতে পারে।