আমি যখন 3D ও গ্রাফিক্স এবং গেম ইঞ্জিন বিকাশের জন্য একটি অনলাইন ডাউনলোডযোগ্য ভিডিও টিউটোরিয়াল দিয়েছি তখন আধুনিক ওপেনএল-এর সাথে কাজ করছি। আমরা volatile
আমাদের ক্লাসগুলির মধ্যে একটিতে ব্যবহার করেছি । টিউটোরিয়াল ওয়েবসাইটটি এখানে পাওয়া যাবে এবং volatile
কীওয়ার্ডের সাথে কাজ করা Shader Engine
ভিডিওটি 98 নম্বর সিরিজের ভিডিওতে পাওয়া যাবে These এই কাজগুলি আমার নিজের নয় তবে অনুমোদিত হয়েছে Marek A. Krzeminski, MASc
এবং এটি ভিডিও ডাউনলোড পৃষ্ঠার একটি অংশ।
আপনি যদি তার ওয়েবসাইটে সাবস্ক্রাইব হন এবং এই ভিডিওর মধ্যে তার ভিডিওর অ্যাক্সেস রয়েছে তবে তিনি এই ব্যবহারটি সম্পর্কিত এই নিবন্ধটি উল্লেখ Volatile
করেছেনmultithreading
প্রোগ্রামিং।
উদ্বায়ী: মাল্টিথ্রেডেড প্রোগ্রামার সেরা বন্ধু
আন্দ্রেই আলেকজান্দ্রেস্কু, ফেব্রুয়ারি 01, 2001 By
অস্থির কীওয়ার্ডটি সংকলক অপ্টিমাইজেশানগুলি রোধ করার জন্য তৈরি করা হয়েছিল যা নির্দিষ্ট অ্যাসিনক্রোনাস ইভেন্টের উপস্থিতিতে কোডকে ভুলভাবে রেন্ডার করতে পারে।
আমি আপনার মেজাজ নষ্ট করতে চাই না, তবে এই কলামটি বহুবিধ প্রোগ্রামিংয়ের ভয়ঙ্কর বিষয়টিকে সম্বোধন করে। যদি - জেনেরিকের পূর্বের কিস্তি হিসাবে বলা হয় - ব্যতিক্রম-নিরাপদ প্রোগ্রামিং কঠিন, এটি বহুবিধ প্রোগ্রামিংয়ের তুলনায় বাচ্চার খেলা।
একাধিক থ্রেড ব্যবহার করে প্রোগ্রামগুলি সাধারণভাবে লেখার জন্য, সঠিক প্রমাণিত করতে, ডিবাগ করতে, বজায় রাখা, এবং কৃত্রিমভাবে কুখ্যাত। ভুল মাল্টিথ্রেডেড প্রোগ্রামগুলি বছরের পর বছর ধরে কোনও ত্রুটি ছাড়াই চলতে পারে, কেবল অপ্রত্যাশিতভাবে অ্যামোক চালাতে কারণ কিছু জটিল সময় শর্ত পূরণ করা হয়েছে।
বলা বাহুল্য, বহু প্রোগ্রামার কোড লেখার জন্য একজন প্রোগ্রামার তার সমস্ত সহায়তা পেতে পারেন। এই কলামটি জাতি অবস্থার উপর দৃষ্টি নিবদ্ধ করে - মাল্টিথ্রেডেড প্রোগ্রামগুলির একটি সাধারণ উত্স - এবং কীভাবে সেগুলি এড়ানো যায় সে সম্পর্কে অন্তর্দৃষ্টি এবং সরঞ্জাম সরবরাহ করে এবং আশ্চর্যরকমভাবে, সংকলকটি আপনাকে এটিতে সহায়তা করার জন্য কঠোর পরিশ্রম করে।
শুধু একটি ছোট কীওয়ার্ড
যদিও থ্রেডের ক্ষেত্রে সি এবং সি ++ স্ট্যান্ডার্ড উভয়ই স্পষ্টতই নীরব, তারা অস্থায়ী কীওয়ার্ডের আকারে, মাল্টিথ্রেডিংয়ের জন্য কিছুটা ছাড় দেয়।
ঠিক এর সুপরিচিত কাউন্টার পার্ট কনস্টের মতোই, উদ্বায়ী একটি টাইপ সংশোধক। এটি বিভিন্ন থ্রেডে অ্যাক্সেস এবং সংশোধিত ভেরিয়েবলগুলির সাথে একযোগে ব্যবহার করার উদ্দেশ্যে। মূলত, অস্থির ছাড়া, হয় বহুবিধ প্রোগ্রাম লিখতে অসম্ভব হয়ে যায়, বা সংকলকটি বিশাল অপ্টিমাইজেশনের সুযোগগুলি নষ্ট করে। একটি ব্যাখ্যা ক্রমযুক্ত।
নিম্নলিখিত কোড বিবেচনা করুন:
class Gadget {
public:
void Wait() {
while (!flag_) {
Sleep(1000);
}
}
void Wakeup() {
flag_ = true;
}
...
private:
bool flag_;
};
গ্যাজেটের :: উপরে অপেক্ষা করুন এর উদ্দেশ্য হ'ল প্রতি সেকেন্ডে ফ্ল্যাগ_ সদস্য ভেরিয়েবলটি পরীক্ষা করা এবং যখন সেই পরিবর্তনশীলটি অন্য থ্রেড দ্বারা সত্য হয়ে যায় তখন ফিরে আসা। কমপক্ষে এটির প্রোগ্রামারটি যা ইচ্ছা করেছিল তা হ'ল তবে হায় আফসোসটি ভুল।
ধরুন যে সংকলকটি পরিসংখ্যান করেছেন যে স্লিপ (1000) একটি বাহ্যিক লাইব্রেরিতে কল যা সম্ভবত সদস্যের পরিবর্তনশীল ফ্ল্যাগ_কে সংশোধন করতে পারে না। তারপরে সংকলকটি সিদ্ধান্তে পৌঁছে যে এটি কোনও রেজিস্টারে ফ্ল্যাগ_কে ক্যাশে করতে পারে এবং ধীর-অন-বোর্ড মেমরির অ্যাক্সেস না করে সেই নিবন্ধটিকে ব্যবহার করতে পারে। এটি একক থ্রেডযুক্ত কোডের জন্য একটি দুর্দান্ত অপ্টিমাইজেশন, তবে এই ক্ষেত্রে এটি নির্ভুলতার ক্ষতি করে: আপনি কিছু গ্যাজেট অবজেক্টের জন্য অপেক্ষা করুন, যদিও অন্য থ্রেডটি ওয়েকআপকে কল করে, অপেক্ষাটি চিরকালের জন্য লুপ হয়ে যাবে। এর কারণ হ'ল পতাকা_র পরিবর্তনটি যে_চিহ্নটিকে চিহ্নিত করে রেজিস্টারটিতে প্রতিফলিত হবে না। অপটিমাইজেশন খুব ... আশাবাদী।
রেজিস্টারগুলিতে ক্যাচারিং ভেরিয়েবলগুলি একটি অত্যন্ত মূল্যবান অপটিমাইজেশন যা বেশিরভাগ সময় প্রয়োগ হয়, তাই এটি নষ্ট করার জন্য দুঃখের বিষয় হবে। সি এবং সি ++ আপনাকে এই জাতীয় ক্যাশে স্পষ্টভাবে অক্ষম করার সুযোগ দেয়। আপনি যদি কোনও ভেরিয়েবলের উপর অস্থির সংশোধক ব্যবহার করেন, সংকলকটি রেজিস্টারগুলিতে সেই পরিবর্তনশীলটিকে ক্যাশে করবে না - প্রতিটি অ্যাক্সেস সেই ভেরিয়েবলের আসল মেমরির অবস্থানটিতে আঘাত করে। সুতরাং গ্যাজেটের অপেক্ষা / ওয়েকআপ কম্বো কাজ করতে আপনাকে যা করতে হবে তা হ'ল সঠিকভাবে পতাকা_কে যোগ্য করে তোলা:
class Gadget {
public:
... as above ...
private:
volatile bool flag_;
};
অযৌক্তিকতার যুক্তি এবং ব্যবহারের সর্বাধিক ব্যাখ্যা এখানে থামে এবং একাধিক থ্রেডে আপনি যে আদিম ধরণের ব্যবহার করেন তা অস্থির-যোগ্যতা অর্জনের পরামর্শ দেয়। যাইহোক, অস্থির সাথে আপনি আরও অনেক কিছু করতে পারেন, কারণ এটি সি ++ এর দুর্দান্ত টাইপের সিস্টেমের একটি অংশ।
ব্যবহারকারী-সংজ্ঞায়িত প্রকারের সাথে অস্থির ব্যবহার
আপনি আদিম প্রকারগুলিই নয়, কেবল ব্যবহারকারী-সংজ্ঞায়িত প্রকারকেও অস্থির-যোগ্য করতে পারেন। সেক্ষেত্রে অস্থিরতা কনস্টের অনুরূপ উপায়ে পরিবর্তন করে। (আপনি একই সাথে একই ধরণের কনস্ট এবং অস্থির প্রয়োগ করতে পারেন))
কনস্টের বিপরীতে, উদ্বায়ী আদিম ধরণের এবং ব্যবহারকারী-সংজ্ঞায়িত প্রকারের মধ্যে বৈষম্য। যথা, শ্রেণীর বিপরীতে, আদিম প্রকারগুলি এখনও অস্থির-যোগ্য যখন তাদের সমস্ত ক্রিয়াকলাপ (সংযোজন, গুণ, অ্যাসাইনমেন্ট ইত্যাদি) সমর্থন করে। উদাহরণস্বরূপ, আপনি একটি অস্থিতিশীল ইন্টকে একটি অস্থির int এর জন্য বরাদ্দ করতে পারেন, তবে আপনি কোনও অস্থির অবজেক্টটিকে একটি অস্থির অবজেক্টকে বরাদ্দ করতে পারবেন না।
আসুন উদাহরণস্বরূপ ব্যবহারকারী-সংজ্ঞায়িত প্রকারগুলিতে কীভাবে অস্থিরতা কাজ করে তা বর্ণনা করি।
class Gadget {
public:
void Foo() volatile;
void Bar();
...
private:
String name_;
int state_;
};
...
Gadget regularGadget;
volatile Gadget volatileGadget;
আপনি যদি মনে করেন যে অস্থির জিনিসগুলি দিয়ে এটি কার্যকর নয় তবে কিছু অবাক হওয়ার জন্য প্রস্তুত করুন।
volatileGadget.Foo();
regularGadget.Foo();
volatileGadget.Bar();
একটি অ-যোগ্যতাসম্পন্ন টাইপ থেকে এর অস্থির প্রতিরূপে রূপান্তর তুচ্ছ। তবে কনস্টের মতোই, আপনি ট্রিপটি অস্থির থেকে অ-যোগ্যতাসম্পন্নের দিকে ফেরাতে পারবেন না। আপনার অবশ্যই একটি castালাই ব্যবহার করা উচিত:
Gadget& ref = const_cast<Gadget&>(volatileGadget);
ref.Bar();
একটি অস্থির-যোগ্য শ্রেণি কেবল তার ইন্টারফেসের একটি উপসেটটিতে অ্যাক্সেস দেয়, একটি উপসেট যা শ্রেণি প্রয়োগকারীটির নিয়ন্ত্রণে থাকে। ব্যবহারকারীরা কেবল কনস্টাস্ট্রাস্ট ব্যবহার করে এই ধরণের ইন্টারফেসে সম্পূর্ণ অ্যাক্সেস পেতে পারেন। তদতিরিক্ত, স্থিরতার মতো, অস্থিরতা শ্রেণি থেকে তার সদস্যদের কাছে প্রচার করে (উদাহরণস্বরূপ, ভোল্টাইলগ্যাজেট.নাম_ এবং ভোল্টাইলগ্যাজেট.স্টেট_টি অস্থির পরিবর্তনশীল)।
অস্থির, সমালোচনা বিভাগ এবং রেস শর্তসমূহ
মাল্টিথ্রেডেড প্রোগ্রামগুলির মধ্যে সবচেয়ে সহজ এবং সর্বাধিক ব্যবহৃত সিঙ্ক্রোনাইজেশন ডিভাইস হ'ল মিউটেক্স। একটি মিউটেক্স অধিগ্রহণ এবং প্রকাশের আদিমতার প্রকাশ করে। একবার আপনি কোনও থ্রেডে অ্যাকুইয়ারকে কল করার পরে, অ্যাকুইয়ারকে ফোন করা অন্য কোনও থ্রেড ব্লক হয়ে যাবে। পরে, যখন সেই থ্রেডটি রিলিজ কল করে, তখন অবশ্যই একটি অ্যাকায়ার কলটিতে অবরুদ্ধ একটি থ্রেড প্রকাশিত হবে। অন্য কথায়, প্রদত্ত মিটেক্সের জন্য, একুইয়ারে কল এবং রিলিজের কলের মধ্যে একটি মাত্র থ্রেড প্রসেসরের সময় পেতে পারে। অ্যাকুইয়ারে কল এবং রিলিজের কলের মধ্যে সম্পাদনকারী কোডকে একটি সমালোচনা বিভাগ বলা হয়। (উইন্ডোজ পরিভাষাটি কিছুটা বিভ্রান্তিকর কারণ এটি মুটেক্সকে নিজেই একটি সমালোচনা বিভাগ বলেছে, যখন "মুটেক্স" আসলে একটি আন্ত-প্রক্রিয়া মিটেক্স। তাদের থ্রেড মিটেক্স এবং প্রসেস মিটেক্স বলা হত তবে ভাল হত))
শব্দের শর্তগুলির বিরুদ্ধে ডেটা সুরক্ষার জন্য শব্দের ব্যবহার করা হয়। সংজ্ঞা অনুসারে, একটি রেসের শর্ত ঘটে যখন ডেটাতে আরও থ্রেডের প্রভাব থ্রেডগুলি কীভাবে নির্ধারিত হয় তার উপর নির্ভর করে। যখন দুটি বা ততোধিক থ্রেড একই ডেটা ব্যবহারের জন্য প্রতিযোগিতা করে তখন রেসের পরিস্থিতি উপস্থিত হয়। যেহেতু থ্রেডগুলি সময় মতো নির্বিচারে মুহুর্তগুলিতে একে অপরকে বাধা দিতে পারে, তাই ডেটা দূষিত বা ভুল ব্যাখ্যা করা যেতে পারে। ফলস্বরূপ, পরিবর্তনগুলি এবং কখনও কখনও ডেটাতে অ্যাক্সেসগুলি অবশ্যই গুরুত্বপূর্ণ বিভাগগুলির সাথে সাবধানে সুরক্ষিত করা উচিত। অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিংয়ে সাধারণত এটির অর্থ হ'ল আপনি একটি ক্লাসে একটি মিটেক্সকে সদস্য ভেরিয়েবল হিসাবে সঞ্চয় করেন এবং যখনই আপনি এই শ্রেণীর অবস্থানে প্রবেশ করেন তখন এটি ব্যবহার করুন।
অভিজ্ঞ মাল্টিথ্রেডেড প্রোগ্রামাররা উপরের দুটি অনুচ্ছেদটি পড়তে হতে পারে তবে তাদের উদ্দেশ্য একটি বুদ্ধিজীবী ওয়ার্কআউট সরবরাহ করা, কারণ এখন আমরা অস্থির সংযোগের সাথে লিঙ্ক করব। আমরা সি ++ প্রকারের পৃথিবী এবং থ্রেডিং শব্দার্থবিজ্ঞানের জগতের মধ্যে সমান্তরাল অঙ্কন করে এটি করি।
- একটি সমালোচনা বিভাগের বাইরে, যে কোনও থ্রেড যে কোনও সময় অন্য কোনওটিকে বাধা দিতে পারে; কোনও নিয়ন্ত্রণ নেই, ফলস্বরূপ একাধিক থ্রেড থেকে অ্যাক্সেসযোগ্য ভেরিয়েবলগুলি অস্থির। এটি উদ্বায়ী এর মূল অভিপ্রায় অনুসারে - একসাথে একাধিক থ্রেড দ্বারা অযৌক্তিকরূপে ক্যাচিং মানগুলি সংকলককে প্রতিরোধ করা।
- একটি মিউটেক্স দ্বারা সংজ্ঞায়িত করা একটি সমালোচনা বিভাগের ভিতরে, কেবল একটি থ্রেডের অ্যাক্সেস রয়েছে। ফলস্বরূপ, একটি সমালোচনা বিভাগের ভিতরে, এক্সিকিউটিভ কোডটিতে একক থ্রেডযুক্ত শব্দার্থক থাকে। নিয়ন্ত্রিত ভেরিয়েবলটি আর অস্থির নয় - আপনি অস্থির যোগ্যতাসম্পন্ন বাছাই করতে পারেন।
সংক্ষেপে, থ্রেডগুলির মধ্যে ভাগ করা ডেটা একটি সমালোচনামূলক বিভাগের বাইরে ধারণাগতভাবে অস্থির এবং সমালোচনামূলক বিভাগের অভ্যন্তরে অ-উদ্বায়ী is
আপনি একটি নিঃশব্দ লক করে একটি গুরুত্বপূর্ণ বিভাগ প্রবেশ করান। আপনি কনস্ট_কাস্ট প্রয়োগ করে কোনও প্রকার থেকে অস্থির যোগ্য বাছাইকারীকে সরিয়ে দিন। যদি আমরা এই দুটি অপারেশনকে একসাথে পরিচালনা করতে পরিচালিত করি তবে আমরা সি ++ এর টাইপ সিস্টেম এবং একটি অ্যাপ্লিকেশনটির থ্রেডিং শব্দার্থের মধ্যে একটি সংযোগ তৈরি করি। আমরা আমাদের জন্য সংকলক চেক রেসের শর্ত তৈরি করতে পারি।
লকিংপি.টি.
আমাদের এমন একটি সরঞ্জাম দরকার যা একটি মুটেক্স অধিগ্রহণ এবং কনস্ট_কাস্ট সংগ্রহ করে। আসুন একটি লকিংপিটিআর ক্লাস টেম্পলেট বিকাশ করুন যা আপনি একটি অস্থির অবজেক্ট অবজেক্ট এবং একটি মিটেক্স এমটিএক্স দিয়ে আরম্ভ করেন। তার জীবদ্দশায়, একটি লকিংপিআরটি এমটিএক্স অর্জন করেছে। এছাড়াও, লকিংপিআরটি অস্থির-স্ট্রিপড আপজে অ্যাক্সেসের প্রস্তাব দেয়। অপারেটর-> এবং অপারেটর * এর মাধ্যমে অ্যাক্সেসটি একটি স্মার্ট পয়েন্টার ফ্যাশনে দেওয়া হয়। কনস্ট_কাস্টটি লকিংপিটিআরের অভ্যন্তরে সঞ্চালিত হয়। Castালাই শব্দার্থগতভাবে বৈধ কারণ লকিংপিআরটি তার জীবদ্দশায় অর্জিত মুটেক্সকে রাখে।
প্রথমে আসুন একটি ক্লাস মিটেক্সের কঙ্কালটি সংজ্ঞায়িত করুন যার সাহায্যে লকিংপিআরটি কাজ করবে:
class Mutex {
public:
void Acquire();
void Release();
...
};
লকিংপিআরটি ব্যবহার করতে, আপনি আপনার অপারেটিং সিস্টেমের নেটিভ ডেটা স্ট্রাকচার এবং আদিম ফাংশনগুলি ব্যবহার করে Mutex প্রয়োগ করেন।
লকিংপিআরটি নিয়ন্ত্রিত ভেরিয়েবলের প্রকারের সাথে সঞ্চারিত হয়। উদাহরণস্বরূপ, আপনি যদি একটি উইজেটটি নিয়ন্ত্রণ করতে চান তবে আপনি একটি লকিংপিআরটি ব্যবহার করেন যা আপনি ভোল্টাইল উইজেটের টাইপের একটি চলক দিয়ে আরম্ভ করেন।
লকিংপিআরটির সংজ্ঞাটি খুব সহজ। লকিংপিআরটি একটি অপ্রয়োজনীয় স্মার্ট পয়েন্টার প্রয়োগ করে। এটি সম্পূর্ণরূপে কনস্ট_কাস্ট এবং সমালোচনামূলক বিভাগ সংগ্রহের দিকে মনোনিবেশ করে।
template <typename T>
class LockingPtr {
public:
LockingPtr(volatile T& obj, Mutex& mtx)
: pObj_(const_cast<T*>(&obj)), pMtx_(&mtx) {
mtx.Lock();
}
~LockingPtr() {
pMtx_->Unlock();
}
T& operator*() {
return *pObj_;
}
T* operator->() {
return pObj_;
}
private:
T* pObj_;
Mutex* pMtx_;
LockingPtr(const LockingPtr&);
LockingPtr& operator=(const LockingPtr&);
};
এর সরলতা সত্ত্বেও, লকিংপিআরটি সঠিক মাল্টিথ্রেডেড কোড লেখার জন্য একটি খুব দরকারী সহায়তা। আপনার থ্রেডগুলির মধ্যে ভাগ হওয়া অবজেক্টগুলি অস্থায়ী হিসাবে সংজ্ঞায়িত করা উচিত এবং সেগুলির সাথে কনস্ট_কাস্ট কখনও ব্যবহার করবেন না - সর্বদা লকিংপিআরটি স্বয়ংক্রিয় বস্তু ব্যবহার করুন। এর উদাহরণ দিয়ে উদাহরণ দিয়ে দেখি।
বলুন আপনার কাছে দুটি থ্রেড রয়েছে যা ভেক্টর অবজেক্টটি ভাগ করে:
class SyncBuf {
public:
void Thread1();
void Thread2();
private:
typedef vector<char> BufT;
volatile BufT buffer_;
Mutex mtx_;
};
একটি থ্রেড ফাংশনের অভ্যন্তরে, আপনি বাফার_ সদস্য ভেরিয়েবলের নিয়ন্ত্রিত অ্যাক্সেস পেতে কেবল একটি লকিংপিআরটি ব্যবহার করেন:
void SyncBuf::Thread1() {
LockingPtr<BufT> lpBuf(buffer_, mtx_);
BufT::iterator i = lpBuf->begin();
for (; i != lpBuf->end(); ++i) {
... use *i ...
}
}
কোডটি লিখতে এবং বুঝতে খুব সহজ - যখনই আপনাকে বাফার_ ব্যবহার করতে হবে, আপনাকে অবশ্যই এটির দিকে নির্দেশ করে একটি লকিংপিআরটি তৈরি করতে হবে। একবার আপনি এটি করার পরে, আপনি ভেক্টরের পুরো ইন্টারফেসে অ্যাক্সেস পাবেন।
সুন্দর অংশটি হ'ল আপনি যদি কোনও ভুল করেন তবে সংকলকটি এটি উল্লেখ করবে:
void SyncBuf::Thread2() {
BufT::iterator i = buffer_.begin();
for ( ; i != lpBuf->end(); ++i ) {
... use *i ...
}
}
আপনি বাফার_এর কোনও ক্রিয়াকলাপ অ্যাক্সেস করতে পারবেন না যতক্ষণ না আপনি কনস্টাকাস্ট প্রয়োগ করেন বা লকিংপিটিআর ব্যবহার না করেন। পার্থক্যটি হ'ল লকিংপিআরটি কনস্টাস্টকস্টটিকে অস্থায়ী ভেরিয়েবলগুলিতে প্রয়োগ করার একটি অর্ডারযুক্ত উপায় সরবরাহ করে।
লকিংপিআরটি উল্লেখযোগ্যভাবে উদ্ভুত। আপনার যদি কেবল একটি ফাংশন কল করতে হয় তবে আপনি একটি নামবিহীন অস্থায়ী লকিংপিআরটি অবজেক্ট তৈরি করতে পারেন এবং এটি সরাসরি ব্যবহার করতে পারেন:
unsigned int SyncBuf::Size() {
return LockingPtr<BufT>(buffer_, mtx_)->size();
}
আদিম প্রকারগুলিতে ফিরে যান
আমরা দেখেছি যে নিয়ন্ত্রণহীন অ্যাক্সেসের বিরুদ্ধে কীভাবে দুর্দান্তভাবে অস্থিরতা সংরক্ষণ করে এবং কীভাবে লকিংপিআরটি থ্রেড-নিরাপদ কোড লেখার সহজ এবং কার্যকর উপায় সরবরাহ করে। আসুন এখন ফিরে আসুন আদিম ধরণেরগুলিতে, যা অস্থির দ্বারা আলাদাভাবে চিকিত্সা করা হয়।
আসুন একটি উদাহরণ বিবেচনা করুন যেখানে একাধিক থ্রেডগুলি টাইপ ইন্টের একটি পরিবর্তনশীল ভাগ করে।
class Counter {
public:
...
void Increment() { ++ctr_; }
void Decrement() { —ctr_; }
private:
int ctr_;
};
যদি বৃদ্ধি এবং হ্রাস বিভিন্ন থ্রেড থেকে কল করতে হয়, উপরের খণ্ডটি বগি। প্রথমত, সিটিআর_ অবশ্যই অস্থির হতে হবে। দ্বিতীয়ত, এমনকি আপাতদৃষ্টিতে পারমাণবিক অপারেশন যেমন ++ সিটিআর_ আসলে তিন ধাপের অপারেশন। মেমরি নিজেই কোন গাণিতিক ক্ষমতা আছে। কোনও ভেরিয়েবলকে বাড়ানোর সময়, প্রসেসর:
- একটি রেজিস্টার যে পরিবর্তনশীল পড়তে
- রেজিস্টারে মান বৃদ্ধি করে
- ফলাফলটি স্মৃতিতে ফিরে আসে
এই তিন-পদক্ষেপের অপারেশনটিকে আরএমডাব্লু (রিড-মডিফাই-রাইটিং) বলা হয়। আরএমডাব্লু অপারেশনের অংশ পরিবর্তিত করার সময়, অন্যান্য প্রসেসরের মেমরিতে অ্যাক্সেস দেওয়ার জন্য বেশিরভাগ প্রসেসর মেমরি বাসটি মুক্ত করে।
যদি সেই সময় অন্য প্রসেসর একই ভেরিয়েবলের উপর আরএমডাব্লু অপারেশন করে তবে আমাদের একটি রেসের শর্ত রয়েছে: দ্বিতীয় রাইটটি প্রথমটির প্রভাবকে ওভাররাইট করে।
এড়াতে, আপনি আবার লকিংপিটিআরের উপর নির্ভর করতে পারেন:
class Counter {
public:
...
void Increment() { ++*LockingPtr<int>(ctr_, mtx_); }
void Decrement() { —*LockingPtr<int>(ctr_, mtx_); }
private:
volatile int ctr_;
Mutex mtx_;
};
এখন কোডটি সঠিক, তবে সিঙ্কবুফের কোডের সাথে তুলনা করার সময় এর মানটি নিম্নমানের। কেন? কাউন্টার সহ, আপনি ভুলভাবে সিটিআর_কে সরাসরি (লক না করে) অ্যাক্সেস করলে সংকলক আপনাকে সতর্ক করবে না। সংকলক সিটিটি_টি অস্থির হলে ++ সিটিআর_ সংকলন করে, যদিও উত্পন্ন কোডটি কেবল ভুল incor সংকলকটি আর আপনার মিত্র নয়, এবং কেবলমাত্র আপনার মনোযোগ আপনাকে জাতিদের অবস্থা এড়াতে সহায়তা করতে পারে।
তারপর আপনি কি করা উচিত? আপনি উচ্চ-স্তরের কাঠামোগুলিতে যে আদিম ডেটা ব্যবহার করেন তা কেবল আবদ্ধ করে এবং সেই কাঠামোগুলির সাথে অস্থিরতা ব্যবহার করে। বিস্ময়করভাবে, বিল্ট-ইনগুলির সাথে সরাসরি অস্থির ব্যবহার করা আরও খারাপ, তবুও প্রাথমিকভাবে এটি ছিল অস্থির ব্যবহারের অভিপ্রায়!
অস্থায়ী সদস্য ফাংশন
এখনও অবধি, আমাদের ক্লাসগুলি রয়েছে যা সামগ্রিকভাবে অস্থির ডেটা সদস্যদের রয়েছে; এখন আসুন ক্লাসগুলি ডিজাইনের কথা ভাবা যাক ফলস্বরূপ বৃহত্তর অবজেক্টের অংশ হয়ে থ্রেডগুলির মধ্যে ভাগ করা হবে। অস্থায়ী সদস্য ফাংশনগুলি এখানে দুর্দান্ত সহায়তা করতে পারে is
আপনার শ্রেণি নকশা করার সময়, আপনি কেবল সেই সদস্য ফাংশনগুলিই নিরাপদ যা থ্রেড নিরাপদ। আপনাকে অবশ্যই ধরে নিতে হবে যে বাইরের কোডটি যে কোনও সময় যে কোনও কোড থেকে অস্থির ফাংশনগুলিতে কল করবে। ভুলে যাবেন না: অস্থির বিনামূল্যে বিনামূল্যে মাল্টিথ্রেডেড কোড এবং সমালোচনামূলক বিভাগের সমান নয়; অ-উদ্বায়ী একক থ্রেডেড দৃশ্যের সমতুল্য বা সমালোচনামূলক বিভাগের ভিতরে।
উদাহরণস্বরূপ, আপনি একটি শ্রেণি উইজেট সংজ্ঞায়িত করেছেন যা দুটি বৈকল্পিকতে একটি ক্রিয়াকলাপ প্রয়োগ করে - একটি থ্রেড-নিরাপদ এবং একটি দ্রুত, অরক্ষিত।
class Widget {
public:
void Operation() volatile;
void Operation();
...
private:
Mutex mtx_;
};
ওভারলোডিংয়ের ব্যবহারটি লক্ষ্য করুন। এখন উইজেটের ব্যবহারকারী অস্থায়ী বস্তুর জন্য এবং থ্রেড সুরক্ষা পেতে, বা নিয়মিত বস্তুর জন্য এবং গতি অর্জন করতে হয় হয় ইউনিফর্ম সিনট্যাক্স ব্যবহার করে অপারেশন করতে পারেন। ব্যবহারকারীকে অবশ্যই ভাগ করা উইজেট অবজেক্টগুলিকে অস্থির হিসাবে সংজ্ঞায়িত করার বিষয়ে সতর্ক থাকতে হবে।
একটি অস্থির সদস্য ফাংশন প্রয়োগ করার সময়, প্রথম অপারেশনটি সাধারণত এটি একটি লকিংপিটিআর দিয়ে লক করা হয়। তারপরে অ-উদ্বায়ী ভাইবাল ব্যবহার করে কাজটি করা হয়:
void Widget::Operation() volatile {
LockingPtr<Widget> lpThis(*this, mtx_);
lpThis->Operation();
}
সারসংক্ষেপ
মাল্টিথ্রেডেড প্রোগ্রামগুলি লেখার সময় আপনি আপনার সুবিধার জন্য উদ্বায়ী ব্যবহার করতে পারেন। আপনাকে অবশ্যই নিম্নলিখিত বিধিগুলিকে আঁকড়ে থাকতে হবে:
- সমস্ত ভাগ করা অবজেক্টগুলি অস্থির হিসাবে সংজ্ঞায়িত করুন।
- আদিম ধরণের সাথে সরাসরি অস্থির ব্যবহার করবেন না।
- ভাগ করা ক্লাস সংজ্ঞায়িত করার সময়, থ্রেড সুরক্ষা প্রকাশ করতে অস্থায়ী সদস্য ফাংশন ব্যবহার করুন।
আপনি যদি এটি করেন এবং আপনি যদি সাধারণ জেনেরিক উপাদান লকিংপিটিআর ব্যবহার করেন তবে আপনি থ্রেড-সেফ কোড লিখতে পারেন এবং বর্ণের পরিস্থিতি সম্পর্কে খুব কম চিন্তা করতে পারেন, কারণ সংকলকটি আপনার জন্য উদ্বেগ প্রকাশ করবে এবং যেখানে আপনি ভুল সেগুলি স্পষ্টভাবে চিহ্নিত করবে।
আমি বেশ কয়েকটি প্রকল্পের সাথে দুর্দান্তভাবে প্রভাব ফেলতে উদ্বায়ী এবং লকিংপিআরটি ব্যবহারের সাথে জড়িত আছি। কোডটি পরিষ্কার এবং বোধগম্য। আমি বেশ কয়েকটা ডেডলকগুলি স্মরণ করি, তবে আমি জাতি শর্তের তুলনায় ডেডলকগুলি পছন্দ করি কারণ সেগুলি ডিবাগ করা খুব সহজ। কার্যত দৌড় শর্ত সম্পর্কিত কোনও সমস্যা ছিল না। তবে আপনি কখনই জানেন না।
স্বীকৃতি
জেমস কানজে এবং সোরিন জিয়ানুকে ধন্যবাদ জানাই যারা অন্তর্দৃষ্টিপূর্ণ ধারণাগুলিতে সহায়তা করেছিলেন।
আন্দ্রে আলেকজান্দ্রেস্কু সিয়াটল, ডাব্লিউএ-তে ভিত্তিক রিয়েল নেটওয়ার্কস ইনক (www.realnetworks.com) এর ডেভলপমেন্ট ম্যানেজার এবং প্রশংসিত বই মডার্ন সি ++ ডিজাইনের লেখক। Www.moderncppdesign.com এ তার সাথে যোগাযোগ করা যেতে পারে। আন্দ্রেই সি ++ সেমিনার (www.gotw.ca/cpp_seminar) এর অন্যতম বৈশিষ্ট্যযুক্ত প্রশিক্ষকও।
এই নিবন্ধটি কিছুটা তারিখযুক্ত হতে পারে তবে এটি আমাদের জন্য জাতিগত অবস্থার জন্য সংকলক পরীক্ষা করার সময় ইভেন্টগুলিকে অযৌক্তিক রাখতে সাহায্য করার জন্য মাল্টিথ্রেডেড প্রোগ্রামিংয়ের সাথে অস্থির পরিবর্তনকারী ব্যবহারের একটি দুর্দান্ত ব্যবহারের দিকে ভাল অন্তর্দৃষ্টি দেয়। এটি মেমোরি বেড়া তৈরি সম্পর্কে ওপিএসের আসল প্রশ্নের সরাসরি উত্তর নাও দিতে পারে, তবে আমি মাল্টিথ্রেডেড অ্যাপ্লিকেশনগুলির সাথে কাজ করার সময় অস্থির ব্যবহারের ভাল ব্যবহারের জন্য একটি দুর্দান্ত রেফারেন্স হিসাবে এটি অন্যদের কাছে উত্তর হিসাবে পোস্ট করার পছন্দ করি।