সাম্প্রতিক প্রোগ্রামিংয়ের প্রসঙ্গে "ডেটা রেস" এবং "রেসের শর্ত" আসলে একই জিনিস


উত্তর:


143

না, তারা একই জিনিস নয়। তারা একে অপরের উপসেট নয়। এগুলি একে অপরের জন্য প্রয়োজনীয়ও নয়, পর্যাপ্ত শর্তও নয়।

ডেটা রেসের সংজ্ঞাটি বেশ পরিষ্কার, এবং তাই এর আবিষ্কারটি স্বয়ংক্রিয়ভাবে তৈরি করা যায়। একটি ডেটা রেস ঘটে যখন বিভিন্ন থ্রেডের 2 টি নির্দেশ একই মেমরির অবস্থানটিতে অ্যাক্সেস করে, এই অ্যাক্সেসগুলির মধ্যে কমপক্ষে একটি লিখন হয় এবং এমন কোনও সমন্বয় নেই যা এই অ্যাক্সেসগুলির মধ্যে কোনও নির্দিষ্ট ক্রমকে বাধ্যতামূলক করে।

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

নীচের সাধারণ উদাহরণটি বিবেচনা করুন যেখানে এক্স একটি ভাগ করা পরিবর্তনশীল:

Thread 1    Thread 2

 lock(l)     lock(l)
 x=1         x=2
 unlock(l)   unlock(l)

এই উদাহরণস্বরূপ, থ্রেড 1 এবং 2 থেকে এক্স-এ লেখা লেখাগুলি লক দ্বারা সুরক্ষিত রয়েছে, সুতরাং লন্ডগুলি রানটাইমের সময় লকগুলি অর্ডারের দ্বারা প্রয়োগ করা হয় এমন কোনও ক্রমে সর্বদা ঘটে থাকে। অর্থাৎ লেখকরা 'পারমাণবিকতা ভাঙা যায় না; দুজনের মধ্যে সম্পর্কের আগে যে কোনও মৃত্যুদন্ডে লেখার আগে সর্বদা একটি ঘটনা ঘটে। অন্যের প্রাইরির আগে কোনটি লেখা হয় তা আমরা কেবল জানতে পারি না।

লেখকদের মধ্যে কোনও স্থির আদেশ নেই, কারণ লকগুলি এটি সরবরাহ করতে পারে না। যদি প্রোগ্রামগুলির যথাযথতা আপস করা হয়, তখন থ্রেড 2 দ্বারা এক্স-এ লেখার পরে থ্রেড 1-এ লিখার পরে বলুন, আমরা বলি একটি রেসের শর্ত রয়েছে, যদিও প্রযুক্তিগতভাবে কোনও ডেটা রেস নেই।

ডেটা রেসের চেয়ে রেসের অবস্থা সনাক্ত করা এটি অনেক বেশি কার্যকর; তবে এটি অর্জন করাও খুব কঠিন।

বিপরীত উদাহরণ নির্মাণ করাও তুচ্ছ। একটি সাধারণ ব্যাংক লেনদেনের উদাহরণ সহ এই ব্লগ পোস্টটিও পার্থক্যটি খুব ভালভাবে ব্যাখ্যা করে।


4
"ডেটা রেস (...) এমন কোনও সিঙ্ক্রোনাইজেশন নেই যা এই অ্যাক্সেসগুলির মধ্যে কোনও নির্দিষ্ট ক্রমকে বাধ্যতামূলক করে।" আমি একটু বিভ্রান্ত। আপনার উদাহরণে, ক্রিয়াকলাপগুলি উভয় অর্ডারে (উভয় = 1 তবে = 2 বা অন্যদিকে) ঘটতে পারে। কেন এটি একটি ডেটা রেস নয়?
জোসিনালভো

6
@ জোসিনালভো: এটি একটি ডেটা রেসের প্রযুক্তিগত সংজ্ঞার একটি নিদর্শন। মূল বক্তব্যটি হ'ল দুটি প্রবেশের মধ্যে একটি লক রিলিজ এবং একটি লক অর্জন (সম্ভাব্য আদেশগুলির মধ্যে একটির জন্য) থাকবে। সংজ্ঞা অনুসারে, একটি লক রিলিজ এবং একটি লক অর্জন দুটি প্রবেশের মধ্যে একটি অর্ডার স্থাপন করে এবং তাই কোনও ডেটা রেস নেই।
বারিস কাসিকিচি

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

@ বারিসকাসিচ্চি "অর্ডার স্থাপন করে" এর যথার্থ অর্থ নেই, যতক্ষণ না আমি উদ্বিগ্ন। এগুলি কেবল নিসলের শব্দ। আমি সত্যই বিশ্বাস করি না যে "ডেটা রেস" একটি দূরবর্তীভাবে কার্যকর ধারণা, কারণ আক্ষরিক অর্থে প্রতিটি মেমরি অবস্থান যা একাধিক থ্রেড দ্বারা অ্যাক্সেস পায় তাকে "ডেটা রেসে" হিসাবে বিবেচনা করা যেতে পারে
নলডোরিন

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

20

উইকিপিডিয়া অনুসারে, "রেস কন্ডিশন" শব্দটি প্রথম বৈদ্যুতিন লজিক গেটের দিন থেকেই ব্যবহৃত হয়ে আসছে। জাভা প্রসঙ্গে, একটি রেসের শর্তটি যে কোনও সংস্থার সাথে সম্পর্কিত হতে পারে, যেমন একটি ফাইল, নেটওয়ার্ক সংযোগ, থ্রেড পুলের একটি থ্রেড ইত্যাদি etc.

জেএলএস দ্বারা সংজ্ঞায়িত নির্দিষ্ট অর্থের জন্য "ডেটা রেস" শব্দটি সবচেয়ে ভাল সংরক্ষিত ।

সর্বাধিক আকর্ষণীয় কেসটি এমন একটি রেসের শর্ত যা ডেটা রেসের সাথে খুব একই রকম, তবে এখনও এই সাধারণ উদাহরণের মতো একটি নয়:

class Race {
  static volatile int i;
  static int uniqueInt() { return i++; }
}

যেহেতু iঅস্থির, কোনও ডেটা রেস নেই; তবে প্রোগ্রামের নির্ভুলতার দৃষ্টিকোণ থেকে দুটি অপারেশনের অ-পারমাণবিকতার কারণে একটি রেসের শর্ত রয়েছে: পড়ুন i, লিখুন i+1। একাধিক থ্রেড একই মান পেতে পারে uniqueInt


4
আপনি data raceজেএলএস-এর প্রকৃত অর্থ কী তা বর্ণনা করে আপনার উত্তরটিতে একটি লাইন ফেলে দিতে পারেন ?
গিগ

@ জেক "জেএলএস" শব্দটি জেএলএসের সম্পর্কিত বিভাগে একটি হাইপার লিঙ্ক।
মার্কো তোপোলনিক

@ মারকো টপলনিক উদাহরণ দিয়ে আমি কিছুটা বিভ্রান্ত। আপনি কি দয়া করে ব্যাখ্যা করতে পারেন: "যেহেতু আমি অস্থির, কোনও ডেটা রেস নেই"? ভোল্টিলিটি কেবল এটি দৃশ্যমান তবে তবুও নিশ্চিত করেছে: ১) এটি সিঙ্ক্রোনাইজ করা হয়নি এবং একাধিক থ্রেড একই সাথে পড়তে / লিখতে পারে এবং ২) এটি চূড়ান্ত নয় এমন চূড়ান্ত ক্ষেত্র, সুতরাং অনুশীলনে জাভা কনকুরানসি অনুসারে (নীচে পাশাপাশি উদ্ধৃত) , এটি ডেটা রেস এবং রেসের শর্ত নয়, তাই না?
aniliitb10

@ aniliitb10 তাদের প্রসঙ্গটি ছিন্ন হয়ে দ্বিতীয় হাতের উদ্ধৃতিগুলির উপর নির্ভর করার পরিবর্তে আপনার জেএলএস বিভাগ 17.4 পর্যালোচনা করা উচিত আমি আমার উত্তরে লিঙ্ক করেছি। একটি উদ্বায়ী ভেরিয়েবলের অ্যাক্সেস একটি সিঙ্ক্রোনাইজেশন ক্রিয়া হিসাবে defined17.4.2 এ সংজ্ঞায়িত করা হয়।
মার্কো টপলনিক

@ aniliitb10 ভোটাটাইলগুলি ডেটা রেসের কারণ হয় না, কারণ তাদের অ্যাক্সেসগুলি অর্ডার করা যেতে পারে। এটি হল, আপনি তাদের আদেশটি এইভাবে বা সেভাবে যুক্ত করতে পারেন, যা বিভিন্ন ফলাফলের দিকে নিয়ে যায়। ডেটা রেসের সাথে, আপনার কাছে অর্ডার যুক্তির কোনও উপায় নেই। উদাহরণস্বরূপ, প্রতিটি থ্রেডের আই ++ অপারেশন কেবলমাত্র তাদের নিজ নিজ স্থানীয় ক্যাশেড মানের উপর ঘটতে পারে i। বিশ্বব্যাপী আপনার কাছে সেই ক্রিয়াকলাপগুলি অর্ডার করার কোনও উপায় নেই (প্রোগ্রামারের দৃষ্টিকোণ থেকে) - যদি না আপনার কাছে একটি নির্দিষ্ট ভাষার মেমরি মডেল থাকে।
জিয়াও-ফেং লি

3

না, তারা আলাদা এবং তাদের উভয়ই একটির বা তদ্বিপরীত উপসেট নয়

শব্দ রেসের শর্তটি প্রায়শই সম্পর্কিত শব্দ ডেটা রেসের সাথে বিভ্রান্ত হয়, যা যখন সিঙ্ক্রোনাইজেশন কোনও ভাগ করা ননফাইনাল ক্ষেত্রে সমস্ত অ্যাক্সেস সমন্বয় করতে ব্যবহৃত হয় না তখন উদ্ভূত হয়। আপনি যখনই কোনও থ্রেড একটি ভেরিয়েবল লিখেন তখনই আপনি কোনও ডেটা রেসের ঝুঁকি নিয়ে যান যা পরবর্তী কোনও থ্রেড দ্বারা পঠিত হতে পারে বা একটি ভেরিয়েবল পড়ে যা শেষ পর্যন্ত অন্য থ্রেড দ্বারা লিখিত থাকতে পারে যদি উভয় থ্রেড সিঙ্ক্রোনাইজেশন ব্যবহার না করে; ডেটা রেসের কোড সহ জাভা মেমোরি মডেলের অধীনে কোনও কার্যকর সংজ্ঞায়িত শব্দার্থবিজ্ঞান নেই। সমস্ত রেসের শর্তগুলি ডেটা রেস নয়, এবং সমস্ত ডেটা রেসই রেসের শর্ত নয়, তবে তারা উভয়ই সাম্প্রতিক প্রোগ্রামগুলিকে অনির্দেশ্য উপায়ে ব্যর্থ করতে পারে।

দুর্দান্ত বইটি থেকে নেওয়া - জাশুয়া ব্লচ এন্ড কোং এর অনুশীলনে জাভা কনকুরেন্সি


নোট করুন যে প্রশ্নটিতে ভাষা-অজ্ঞাতনীয় ট্যাগ রয়েছে।
মার্টিনকুনেভ

1

টিএল; ডিআর: ডেটা রেস এবং রেসের শর্তের মধ্যে পার্থক্য সমস্যা গঠনের প্রকৃতির উপর নির্ভর করে এবং কোথায় অপরিজ্ঞাত আচরণ এবং সু-সংজ্ঞায়িত তবে অনির্দিষ্ট আচরণের মধ্যে সীমানা আঁকবে। বর্তমান পার্থক্যটি প্রচলিত এবং প্রসেসরের আর্কিটেক্ট এবং প্রোগ্রামিং ভাষার মধ্যবর্তী ইন্টারফেসকে সবচেয়ে ভাল প্রতিফলিত করে।

1. শব্দার্থবিজ্ঞান

ডেটা রেসটি বিশেষত একই মেমরির স্থানে অ-সিঙ্ক্রোনাইজড বিবাদযুক্ত "মেমরি অ্যাক্সেসগুলি" (বা ক্রিয়াগুলি, বা ক্রিয়াকলাপগুলি) বোঝায়। যদি মেমোরি অ্যাক্সেসগুলিতে কোনও বিরোধ না হয়, অপারেশন অর্ডারের ফলে এখনও অনির্দিষ্ট আচরণ ঘটে তবে এটি একটি রেসের শর্ত।

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

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

2. কেন পার্থক্য?

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

আধুনিক আর্কিটেকচারে একটি মেমরি লোড / স্টোরের নির্দেশাবলী সাধারণত "খাঁটি" মেমরি অ্যাক্সেস হিসাবে প্রয়োগ করা হয়, অফ-অফ-অর্ডার পাইপলাইন, জল্পনা, ক্যাশে মাল্টি-লেভেল, সিপিই-রাম আন্তঃসংযোগ, বিশেষত মাল্টি-কোর ইত্যাদির কারণে access অনিয়মিত সময় এবং আদেশের দিকে পরিচালিত করে এমন অনেকগুলি কারণ রয়েছে। প্রতিটি মেমোরি নির্দেশের জন্য অর্ডার কার্যকর করার জন্য বিশাল জরিমানার প্রয়োজন হয়, বিশেষত একটি প্রসেসরের ডিজাইনে যা মাল্টি-কোরকে সমর্থন করে। সুতরাং অর্ডারিং শব্দার্থকে বিভিন্ন বাধা (বা বেড়া) এর মতো অতিরিক্ত নির্দেশাবলী সরবরাহ করা হয়।

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

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

৩. ভাষার স্মৃতি মডেল

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

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

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

4। উপসংহার

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

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