আইওএস-এ কনকারেন্সি একটি বিশাল বিষয়। তাই এই নিবন্ধে আমি সারি এবং গ্র্যান্ড সেন্ট্রাল ডিসপ্যাচ (GCD) ফ্রেমওয়ার্ক সম্পর্কিত একটি উপ-বিষয়কে জুম করতে চাই।
বিশেষ করে, আমি সিরিয়াল এবং সমসাময়িক সারিগুলির মধ্যে পার্থক্যগুলি, সেইসাথে সিঙ্ক্রোনাস এবং অ্যাসিঙ্ক্রোনাস এক্সিকিউশনের মধ্যে পার্থক্যগুলি অন্বেষণ করতে চাই৷
আপনি যদি আগে কখনও GCD ব্যবহার না করে থাকেন, তাহলে এই নিবন্ধটি শুরু করার জন্য একটি দুর্দান্ত জায়গা। আপনার যদি GCD এর সাথে কিছু অভিজ্ঞতা থাকে তবে উপরে উল্লিখিত বিষয়গুলি সম্পর্কে এখনও কৌতূহলী হন, আমি মনে করি আপনি এখনও এটি দরকারী পাবেন। এবং আমি আশা করি আপনি পথ ধরে একটি বা দুটি নতুন জিনিস বাছাই করবেন।
এই নিবন্ধে ধারণাগুলি দৃশ্যমানভাবে প্রদর্শন করার জন্য আমি একটি SwiftUI সহচর অ্যাপ তৈরি করেছি। অ্যাপটিতে একটি মজার ছোট কুইজ রয়েছে যা আমি আপনাকে এই নিবন্ধটি পড়ার আগে এবং পরে চেষ্টা করার জন্য উত্সাহিত করি। এখানে সোর্স কোড ডাউনলোড করুন, অথবা এখানে পাবলিক বিটা পান।
আমি জিসিডি-র একটি ভূমিকা দিয়ে শুরু করব, তারপরে সিঙ্ক, অ্যাসিঙ্ক, সিরিয়াল এবং সমসাময়িক সম্পর্কে বিশদ ব্যাখ্যা দিয়েছি। তারপরে, আমি একযোগে কাজ করার সময় কিছু ত্রুটিগুলি কভার করব। পরিশেষে, আমি একটি সারসংক্ষেপ এবং কিছু সাধারণ পরামর্শ দিয়ে শেষ করব।
পরিচয়
আসুন GCD এবং প্রেরণ সারিগুলির একটি সংক্ষিপ্ত ভূমিকা দিয়ে শুরু করি। নির্দ্বিধায় সিঙ্ক বনাম অ্যাসিঙ্ক এ চলে যান আপনি যদি বিষয়টির সাথে ইতিমধ্যেই পরিচিত হন তাহলে বিভাগ।
সঙ্গতি এবং গ্র্যান্ড সেন্ট্রাল ডিসপ্যাচ
কনকারেন্সি আপনাকে আপনার ডিভাইসে একাধিক CPU কোর আছে এমন সুবিধা নিতে দেয়। এই কোর ব্যবহার করতে, আপনাকে একাধিক থ্রেড ব্যবহার করতে হবে। যাইহোক, থ্রেডগুলি একটি নিম্ন-স্তরের সরঞ্জাম, এবং থ্রেডগুলিকে একটি দক্ষ উপায়ে ম্যানুয়ালি পরিচালনা করা অত্যন্ত কঠিন৷
10 বছরেরও বেশি আগে Apple দ্বারা গ্র্যান্ড সেন্ট্রাল ডিসপ্যাচ তৈরি করা হয়েছিল একটি বিমূর্ততা হিসাবে ডেভেলপারদের ম্যানুয়ালি থ্রেডগুলি তৈরি এবং পরিচালনা না করে মাল্টি-থ্রেডেড কোড লিখতে সহায়তা করার জন্য৷
GCD এর সাথে, Apple একটি অসিঙ্ক্রোনাস ডিজাইন পদ্ধতি নিয়েছে সমস্যার প্রতি সরাসরি থ্রেড তৈরি করার পরিবর্তে, আপনি কাজের কাজগুলি নির্ধারণ করতে GCD ব্যবহার করেন এবং সিস্টেমটি তার সংস্থানগুলির সর্বোত্তম ব্যবহার করে আপনার জন্য এই কাজগুলি সম্পাদন করবে। GCD প্রয়োজনীয় থ্রেডগুলি তৈরি করা পরিচালনা করবে এবং সেই থ্রেডগুলিতে আপনার কাজগুলি নির্ধারণ করবে, থ্রেড পরিচালনার বোঝা বিকাশকারী থেকে সিস্টেমে স্থানান্তর করবে৷
GCD এর একটি বড় সুবিধা হল যে আপনি আপনার সমসাময়িক কোড লিখতে গিয়ে হার্ডওয়্যার সংস্থান সম্পর্কে চিন্তা করতে হবে না। GCD আপনার জন্য একটি থ্রেড পুল পরিচালনা করে, এবং এটি একটি একক-কোর Apple Watch থেকে অনেক-কোর MacBook Pro পর্যন্ত স্কেল করবে৷
ডিসপ্যাচ সারি
এগুলি হল GCD-এর প্রধান বিল্ডিং ব্লক যা আপনাকে সংজ্ঞায়িত প্যারামিটারগুলির একটি সেট ব্যবহার করে কোডের নির্বিচারে ব্লকগুলি সম্পাদন করতে দেয়। প্রেরণের সারিতে থাকা কাজগুলি সর্বদা একটি প্রথম-ইন, প্রথম-আউট (FIFO) ফ্যাশনে শুরু হয়। মনে রাখবেন যে আমি বলেছি শুরু , কারণ আপনার কাজ সমাপ্তির সময় বিভিন্ন কারণের উপর নির্ভর করে, এবং FIFO হওয়ার নিশ্চয়তা দেওয়া হয় না (পরবর্তীতে আরও।)
বিস্তৃতভাবে বলতে গেলে, আপনার জন্য তিনটি ধরণের সারি পাওয়া যায়:
- প্রধান প্রেরণ সারি (ক্রমিক, পূর্ব-নির্ধারিত)
- গ্লোবাল সারি (সমসাময়িক, পূর্ব-নির্ধারিত)
- ব্যক্তিগত সারি (ক্রমিক বা সমসাময়িক হতে পারে, আপনি সেগুলি তৈরি করেন)
প্রতিটি অ্যাপ্লিকেশান একটি প্রধান সারি সহ আসে, যা একটি সিরিয়াল৷ সারি যা মূল থ্রেডে কার্য সম্পাদন করে। এই সারিটি আপনার অ্যাপ্লিকেশনের UI আঁকতে এবং ব্যবহারকারীর ইন্টারঅ্যাকশনের (টাচ, স্ক্রোল, প্যান, ইত্যাদি) সাড়া দেওয়ার জন্য দায়ী যদি আপনি এই সারিটিকে খুব বেশি সময় অবরুদ্ধ করেন, তাহলে আপনার iOS অ্যাপটি জমাট হয়ে যাবে এবং আপনার macOS অ্যাপটি কুখ্যাত সমুদ্র সৈকত প্রদর্শন করবে। বল/স্পিনিং হুইল।
একটি দীর্ঘ-চলমান কাজ (নেটওয়ার্ক কল, গণনামূলকভাবে নিবিড় কাজ, ইত্যাদি) সম্পাদন করার সময়, আমরা একটি ব্যাকগ্রাউন্ড সারিতে এই কাজটি সম্পাদন করার মাধ্যমে UI হিমায়িত করা এড়াই। তারপরে আমরা মূল সারিতে ফলাফল সহ UI আপডেট করি:
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
DispatchQueue.main.async { // UI work
self.label.text = String(data: data, encoding: .utf8)
}
}
}
একটি অঙ্গুষ্ঠের নিয়ম হিসাবে, সমস্ত UI কাজ অবশ্যই প্রধান সারিতে সম্পাদন করতে হবে। যখনই কোনো ব্যাকগ্রাউন্ড থ্রেডে UI কাজ সম্পাদিত হয় তখন সতর্কবার্তা পেতে আপনি Xcode-এ প্রধান থ্রেড চেকার বিকল্পটি চালু করতে পারেন।
মূল সারি ছাড়াও, প্রতিটি অ্যাপে বেশ কয়েকটি পূর্ব-নির্ধারিত সমসাময়িক সারি রয়েছে যেগুলির পরিষেবার গুণমানের বিভিন্ন স্তর রয়েছে (GCD-তে অগ্রাধিকারের একটি বিমূর্ত ধারণা৷)
উদাহরণস্বরূপ, এখানে ইন্টারেক্টিভ ব্যবহারকারীর-এ অ্যাসিঙ্ক্রোনাসভাবে কাজ জমা দেওয়ার কোড রয়েছে (সর্বোচ্চ অগ্রাধিকার) QoS সারি:
DispatchQueue.global(qos: .userInteractive).async {
print("We're on a global concurrent queue!")
}
বিকল্পভাবে, আপনি ডিফল্ট অগ্রাধিকার কল করতে পারেন এইরকম একটি QoS নির্দিষ্ট না করে গ্লোবাল কিউ:
DispatchQueue.global().async {
print("Generic global queue")
}
অতিরিক্তভাবে, আপনি নিম্নলিখিত সিনট্যাক্স ব্যবহার করে আপনার নিজস্ব ব্যক্তিগত সারি তৈরি করতে পারেন:
let serial = DispatchQueue(label: "com.besher.serial-queue")
serial.async {
print("Private serial queue")
}
ব্যক্তিগত সারি তৈরি করার সময়, এটি একটি বর্ণনামূলক লেবেল (যেমন বিপরীত DNS স্বরলিপি) ব্যবহার করতে সহায়তা করে, কারণ এটি আপনাকে Xcode-এর নেভিগেটর, lldb এবং যন্ত্রগুলিতে ডিবাগ করার সময় সহায়তা করবে:
ডিফল্টরূপে, ব্যক্তিগত সারিগুলি হল ক্রমিক৷ (আমি শীঘ্রই এর অর্থ ব্যাখ্যা করব, প্রতিশ্রুতি!) আপনি যদি একটি ব্যক্তিগত সমসাময়িক তৈরি করতে চান queue, আপনি ঐচ্ছিক গুণাবলী এর মাধ্যমে তা করতে পারেন প্যারামিটার:
let concurrent = DispatchQueue(label: "com.besher.serial-queue", attributes: .concurrent)
concurrent.sync {
print("Private concurrent queue")
}
একটি ঐচ্ছিক QoS প্যারামিটারও রয়েছে। আপনার তৈরি করা ব্যক্তিগত সারিগুলি শেষ পর্যন্ত তাদের প্রদত্ত পরামিতিগুলির উপর ভিত্তি করে বিশ্বব্যাপী সমান্তরাল সারিগুলির মধ্যে একটিতে অবতরণ করবে৷
টাস্কে কী আছে?
আমি সারিতে কাজ পাঠানোর উল্লেখ করেছি। টাস্কগুলি আপনি sync
ব্যবহার করে একটি সারিতে জমা দেওয়া কোডের যেকোনো ব্লককে উল্লেখ করতে পারে অথবা async
ফাংশন এগুলি একটি বেনামী বন্ধের আকারে জমা দেওয়া যেতে পারে:
DispatchQueue.global().async {
print("Anonymous closure")
}
অথবা একটি প্রেরণ কাজের আইটেমের ভিতরে যা পরে সঞ্চালিত হয়:
let item = DispatchWorkItem(qos: .utility) {
print("Work item to be executed later")
}
আপনি সিঙ্ক্রোনাসভাবে বা অ্যাসিঙ্ক্রোনাসভাবে প্রেরণ করুন না কেন, এবং আপনি একটি সিরিয়াল বা সমবর্তী সারি চয়ন করুন না কেন, একটি একক টাস্কের ভিতরের সমস্ত কোড লাইন দ্বারা লাইন চালানো হবে। একাধিক মূল্যায়ন করার সময়ই সঙ্গতি প্রাসঙ্গিক কাজ।
উদাহরণস্বরূপ, আপনার যদি একই ভিতরে 3টি লুপ থাকে টাস্ক, এই লুপগুলি সর্বদা হবে ক্রমানুসারে চালান:
DispatchQueue.global().async {
for i in 0..<10 {
print(i)
}
for _ in 0..<10 {
print("?")
}
for _ in 0..<10 {
print("?")
}
}
এই কোডটি সর্বদা 0 থেকে 9 পর্যন্ত দশটি সংখ্যা প্রিন্ট করে, তারপরে দশটি নীল চেনাশোনা, তারপরে দশটি ভাঙা হৃদয়, আপনি সেই বন্ধটি যেভাবে পাঠান না কেন।
পৃথক কাজগুলির নিজস্ব QoS স্তরও থাকতে পারে (ডিফল্টরূপে তারা তাদের সারির অগ্রাধিকার ব্যবহার করে।) কিউ QoS এবং টাস্ক QoS এর মধ্যে এই পার্থক্য কিছু আকর্ষণীয় আচরণের দিকে নিয়ে যায় যা আমরা অগ্রাধিকার বিপরীত বিভাগে আলোচনা করব।
এতক্ষণে আপনি হয়তো ভাবছেন কি সিরিয়াল এবং সমসাময়িক সব সম্পর্কে আপনি হয়ত sync
এর মধ্যে পার্থক্য সম্পর্কেও ভাবছেন এবং async
আপনার কাজ জমা দেওয়ার সময়। এটি আমাদের এই নিবন্ধের মূলে নিয়ে আসে, তাই আসুন এতে ডুব দেওয়া যাক!
সিঙ্ক বনাম অ্যাসিঙ্ক
আপনি যখন একটি কাজকে একটি সারিতে পাঠান, আপনি sync
ব্যবহার করে সিঙ্ক্রোনাস বা অ্যাসিঙ্ক্রোনাসভাবে তা করতে বেছে নিতে পারেন এবং async
প্রেরণ ফাংশন। সিঙ্ক এবং অ্যাসিঙ্ক প্রাথমিকভাবে উৎসকে প্রভাবিত করে জমা দেওয়া টাস্কের, এটি সেই সারি যেখানে এটি জমা দেওয়া হচ্ছে থেকে .
যখন আপনার কোড একটি sync
এ পৌঁছায় বিবৃতি, এটি বর্তমান সারিটিকে ব্লক করবে যতক্ষণ না সেই টাস্কটি সম্পূর্ণ হয়। একবার টাস্ক ফেরত/সম্পূর্ণ হলে, নিয়ন্ত্রণ কলারের কাছে ফেরত দেওয়া হয়, এবং যে কোডটি sync
অনুসরণ করে কাজ চলতে থাকবে।
sync
এর কথা চিন্তা করুন 'ব্লকিং'-এর সমার্থক হিসেবে।
একটি async
অন্যদিকে, বিবৃতিটি বর্তমান সারির ক্ষেত্রে অ্যাসিঙ্ক্রোনাসভাবে কার্যকর করবে এবং async
-এর বিষয়বস্তুর জন্য অপেক্ষা না করে অবিলম্বে কলারের কাছে নিয়ন্ত্রণ ফিরিয়ে দেবে। চালানো বন্ধ. সেই অ্যাসিঙ্ক ক্লোজারের ভিতরের কোডটি ঠিক কখন কার্যকর হবে তার কোনও গ্যারান্টি নেই৷
বর্তমান সারি?
উৎস কি বা বর্তমান তা স্পষ্ট নাও হতে পারে , সারি হল, কারণ এটি সবসময় কোডে স্পষ্টভাবে সংজ্ঞায়িত করা হয় না।
উদাহরণস্বরূপ, যদি আপনি আপনার sync
কল করেন ভিউডিডলোডের ভিতরে বিবৃতি, আপনার বর্তমান সারি হবে প্রধান প্রেরণ সারি। আপনি যদি একই ফাংশনকে একটি URLSession কমপ্লিশন হ্যান্ডলারের ভিতরে কল করেন, তাহলে আপনার বর্তমান সারিটি একটি পটভূমি সারি হবে৷
সিঙ্ক বনাম অ্যাসিঙ্কে ফিরে যাচ্ছি, আসুন এই উদাহরণটি নেওয়া যাক:
DispatchQueue.global().sync {
print("Inside")
}
print("Outside")
// Console output:
// Inside
// Outside
উপরের কোডটি বর্তমান সারিটিকে ব্লক করবে, ক্লোজারে প্রবেশ করবে এবং "বাইরে" প্রিন্ট করার আগে "ভিতরে" প্রিন্ট করে গ্লোবাল কিউতে এর কোডটি কার্যকর করবে। এই অর্ডার নিশ্চিত।
দেখা যাক যদি আমরা async
চেষ্টা করি তাহলে কি হয় পরিবর্তে:
DispatchQueue.global().async {
print("Inside")
}
print("Outside")
// Potential console output (based on QoS):
// Outside
// Inside
আমাদের কোড এখন গ্লোবাল কিউতে ক্লোজার জমা দেয়, তারপর অবিলম্বে পরবর্তী লাইন চালানোর জন্য এগিয়ে যায়। এটা সম্ভাব্য হবে "ভিতরে" এর আগে "বাইরে" মুদ্রণ করুন, তবে এই অর্ডারটি নিশ্চিত নয়। এটি উৎস এবং গন্তব্য সারিগুলির QoS, সেইসাথে সিস্টেম নিয়ন্ত্রণ করে এমন অন্যান্য কারণগুলির উপর নির্ভর করে৷
থ্রেডগুলি হল GCD-এ একটি বাস্তবায়নের বিশদ—আমাদের তাদের উপর সরাসরি নিয়ন্ত্রণ নেই এবং শুধুমাত্র সারি বিমূর্ততা ব্যবহার করে তাদের সাথে মোকাবিলা করতে পারি। তবুও, আমি মনে করি GCD এর সাথে আমাদের সম্মুখীন হতে পারে এমন কিছু চ্যালেঞ্জ বোঝার জন্য থ্রেড আচরণে 'কভারের নীচে উঁকি দেওয়া' কার্যকর হতে পারে৷
উদাহরণস্বরূপ, যখন আপনি sync
ব্যবহার করে একটি টাস্ক জমা দেন , GCD বর্তমান থ্রেডে (কলার)
তবে একটি ব্যতিক্রম আছে, যেটি হল যখন আপনি একটি সিঙ্ক টাস্ককে প্রধান সারিতে জমা দেন—য তা করলে তা সর্বদা মূল থ্রেডে টাস্ক চালাবে এবং কলার নয়। এই আচরণের কিছু প্রভাব থাকতে পারে যা আমরা অগ্রাধিকার বিপরীত বিভাগে অন্বেষণ করব৷
কোনটি ব্যবহার করবেন?
একটি সারিতে কাজ জমা দেওয়ার সময়, অ্যাপল সিঙ্ক্রোনাস এক্সিকিউশনের উপর অ্যাসিঙ্ক্রোনাস এক্সিকিউশন ব্যবহার করার পরামর্শ দেয়। যাইহোক, এমন পরিস্থিতিতে আছে যেখানে sync
ভাল পছন্দ হতে পারে, যেমন রেসের অবস্থার সাথে মোকাবিলা করার সময়, বা যখন খুব ছোট কাজ সম্পাদন করা হয়। আমি শীঘ্রই এই পরিস্থিতিগুলি কভার করব৷
একটি ফাংশনের অভ্যন্তরে অ্যাসিঙ্ক্রোনাসভাবে কাজ করার একটি বড় পরিণতি হল যে ফাংশনটি আর সরাসরি তার মানগুলি ফেরত দিতে পারে না (যদি তারা অ্যাসিঙ্ক কাজের উপর নির্ভর করে যা করা হচ্ছে)। ফলাফল প্রদানের জন্য এটির পরিবর্তে একটি ক্লোজার/কমপ্লিশন হ্যান্ডলার প্যারামিটার ব্যবহার করতে হবে।
এই ধারণাটি প্রদর্শন করতে, আসুন একটি ছোট ফাংশন গ্রহণ করি যা চিত্রের ডেটা গ্রহণ করে, চিত্রটি প্রক্রিয়া করার জন্য কিছু ব্যয়বহুল গণনা করে, তারপর ফলাফল দেয়:
func processImage(data: Data) -> UIImage? {
guard let image = UIImage(data: data) else { return nil }
// calling an expensive function
let processedImage = upscaleAndFilter(image: image)
return processedImage
}
এই উদাহরণে, ফাংশন upscaleAndFilter(image:)
কয়েক সেকেন্ড সময় লাগতে পারে, তাই আমরা UI জমা হওয়া এড়াতে এটিকে একটি পৃথক সারিতে অফলোড করতে চাই। আসুন ইমেজ প্রসেসিংয়ের জন্য একটি ডেডিকেটেড সারি তৈরি করি, এবং তারপরে অ্যাসিঙ্ক্রোনাসভাবে ব্যয়বহুল ফাংশনটি প্রেরণ করি:
let imageProcessingQueue = DispatchQueue(label: "com.besher.image-processing")
func processImageAsync(data: Data) -> UIImage? {
guard let image = UIImage(data: data) else { return nil }
imageProcessingQueue.async {
let processedImage = upscaleAndFilter(image: image)
return processedImage
}
}
এই কোডের সাথে দুটি সমস্যা আছে। প্রথমত, রিটার্ন স্টেটমেন্টটি অ্যাসিঙ্ক ক্লোজারের ভিতরে থাকে, তাই এটি আর processImageAsync(data:)
-এ একটি মান ফেরত দিচ্ছে না ফাংশন, এবং বর্তমানে কোন উদ্দেশ্য পরিবেশন করে না।
কিন্তু সবচেয়ে বড় সমস্যা হল আমাদের processImageAsync(data:)
ফাংশন আর কোনো মান ফেরত দিচ্ছে না, কারণ ফাংশনটি async
এ প্রবেশ করার আগেই তার শরীরের শেষ প্রান্তে পৌঁছে যায় বন্ধ।
এই ত্রুটিটি ঠিক করতে, আমরা ফাংশনটি সামঞ্জস্য করব যাতে এটি আর সরাসরি কোনও মান ফেরত না দেয়। পরিবর্তে, এটিতে একটি নতুন সমাপ্তি হ্যান্ডলার প্যারামিটার থাকবে যা আমরা একবার আমাদের অ্যাসিঙ্ক্রোনাস ফাংশনটি তার কাজ শেষ করার পরে কল করতে পারি:
let imageProcessingQueue = DispatchQueue(label: "com.besher.image-processing")
func processImageAsync(data: Data, completion: @escaping (UIImage?) -> Void) {
guard let image = UIImage(data: data) else {
completion(nil)
return
}
imageProcessingQueue.async {
let processedImage = self.upscaleAndFilter(image: image)
completion(processedImage)
}
}
এই উদাহরণে স্পষ্ট হিসাবে, ফাংশনটিকে অ্যাসিঙ্ক্রোনাস করার পরিবর্তনটি তার কলারের কাছে প্রচারিত হয়েছে, যাকে এখন ক্লোজারে পাস করতে হবে এবং ফলাফলগুলিও অ্যাসিঙ্ক্রোনাসভাবে পরিচালনা করতে হবে। একটি অ্যাসিঙ্ক্রোনাস টাস্ক প্রবর্তন করে, আপনি সম্ভাব্যভাবে বেশ কয়েকটি ফাংশনের একটি চেইন পরিবর্তন করতে পারেন৷
সঙ্গতি এবং অ্যাসিঙ্ক্রোনাস এক্সিকিউশন আপনার প্রজেক্টে জটিলতা যোগ করে যেমনটি আমরা এইমাত্র পর্যবেক্ষণ করেছি। এই পরোক্ষ ডিবাগিংকে আরও কঠিন করে তোলে। এই কারণেই আপনার ডিজাইনের প্রথম দিকে একত্রিত হওয়ার বিষয়ে চিন্তা করা সত্যিই মূল্য দেয়——এটি এমন কিছু নয় যা আপনি আপনার ডিজাইন চক্রের শেষে ব্যবহার করতে চান।
সিনক্রোনাস এক্সিকিউশন, বিপরীতে, জটিলতা বাড়ায় না। বরং, এটি আপনাকে আগের মতই রিটার্ন স্টেটমেন্ট ব্যবহার চালিয়ে যেতে দেয়। একটি sync
ধারণকারী একটি ফাংশন টাস্কের ভিতরে কোডটি সম্পূর্ণ না হওয়া পর্যন্ত টাস্ক ফিরে আসবে না। তাই এটি একটি সম্পূর্ণ হ্যান্ডলার প্রয়োজন হয় না.
আপনি যদি একটি ছোট কাজ জমা দেন (উদাহরণস্বরূপ, একটি মান আপডেট করা), এটি সিঙ্ক্রোনাসভাবে করার কথা বিবেচনা করুন। এটি কেবলমাত্র আপনার কোডকে সহজ রাখতেই সাহায্য করে না, এটি আরও ভাল কার্য সম্পাদন করবে—Async একটি ওভারহেড বহন করতে পারে বলে মনে করা হয় যা 1ms-এর কম সময় লাগে এমন ছোট কাজগুলির জন্য অ্যাসিঙ্ক্রোনাসভাবে কাজ করার সুবিধার চেয়ে বেশি৷
আপনি যদি একটি বড় টাস্ক জমা দেন, তবে, আমরা উপরে সম্পাদিত ইমেজ প্রসেসিংয়ের মতো, তাহলে কলকারীকে খুব বেশি সময় অবরুদ্ধ না করার জন্য এটি অ্যাসিঙ্ক্রোনাসভাবে করার কথা বিবেচনা করুন৷
একই সারিতে পাঠানো হচ্ছে
যদিও একটি সারি থেকে নিজের মধ্যে একটি টাস্ক অ্যাসিঙ্ক্রোনাসভাবে প্রেরণ করা নিরাপদ (উদাহরণস্বরূপ, আপনি বর্তমান সারিতে .asyncAfter ব্যবহার করতে পারেন), আপনি একটি টাস্ক সিঙ্ক্রোনাসভাবে পাঠাতে পারবেন না একটি সারি থেকে একই সারিতে। এটি করার ফলে একটি অচলাবস্থা দেখা দেবে যা অবিলম্বে অ্যাপটি ক্র্যাশ করবে!
সিঙ্ক্রোনাস কলগুলির একটি চেইন সম্পাদন করার সময় এই সমস্যাটি নিজেকে প্রকাশ করতে পারে যা মূল সারিতে ফিরে যায়। অর্থাৎ, আপনি sync
একটি টাস্ক অন্য সারিতে নিয়ে যায়, এবং যখন টাস্কটি শেষ হয়, এটি ফলাফলগুলিকে আবার মূল সারিতে সিঙ্ক করে, যার ফলে একটি অচলাবস্থা দেখা দেয়। async
ব্যবহার করুন এই ধরনের ক্র্যাশ এড়াতে।
প্রধান সারি ব্লক করা
থেকে সিঙ্ক্রোনাসভাবে কাজগুলি প্রেরণ করা হচ্ছে মূল সারিটি সেই সারিটিকে ব্লক করবে, যার ফলে কাজটি সম্পূর্ণ না হওয়া পর্যন্ত UI হিমায়িত হবে। তাই মূল সারি থেকে সিঙ্ক্রোনাসভাবে কাজ পাঠানো এড়াতে ভাল, যদি না আপনি সত্যিই হালকা কাজ করছেন৷
ক্রমিক বনাম সমবর্তী
সিরিয়াল এবং সমসাময়িক গন্তব্যস্থল কে প্রভাবিত করে — যে সারিতে আপনার কাজ চালানোর জন্য জমা দেওয়া হয়েছে। এটি সিঙ্ক এর বিপরীতে এবং অসিঙ্ক , যা উৎসকে প্রভাবিত করে .
আপনি সেই সারিতে কতগুলি কাজ পাঠান না কেন একটি সিরিয়াল সারি একবারে একাধিক থ্রেডে তার কাজ সম্পাদন করবে না। ফলস্বরূপ, কাজগুলি কেবলমাত্র শুরুই নয়, প্রথম-ইন, প্রথম-আউট ক্রমে সমাপ্ত করার নিশ্চয়তা রয়েছে।
তাছাড়া, আপনি যখন একটি সিরিয়াল সারি ব্লক করেন (একটি sync
ব্যবহার করে কল, সেমাফোর, বা অন্য কোন টুল), ব্লকটি শেষ না হওয়া পর্যন্ত সেই সারির সমস্ত কাজ বন্ধ থাকবে।
একটি সমসাময়িক সারি একাধিক থ্রেড তৈরি করতে পারে এবং সিস্টেমটি ঠিক করে যে কতগুলি থ্রেড তৈরি করা হবে। কাজ সবসময় শুরু FIFO ক্রমানুসারে, কিন্তু পরবর্তী টাস্ক শুরু করার আগে সারিটি কাজগুলি শেষ হওয়ার জন্য অপেক্ষা করে না, তাই সমসাময়িক সারিতে থাকা কাজগুলি যে কোনও ক্রমে শেষ করতে পারে।
আপনি যখন সমসাময়িক সারিতে একটি ব্লকিং কমান্ড সঞ্চালন করেন, তখন এটি এই সারিতে থাকা অন্যান্য থ্রেডগুলিকে ব্লক করবে না। উপরন্তু, যখন একটি সমবর্তী সারি ব্লক হয়ে যায়, তখন এটি থ্রেড বিস্ফোরণের ঝুঁকি চালায় . আমি পরে আরও বিস্তারিতভাবে এটি কভার করব৷
৷আপনার অ্যাপের প্রধান সারিটি সিরিয়াল। সমস্ত বিশ্বব্যাপী প্রাক-সংজ্ঞায়িত সারি সমসাময়িক। আপনার তৈরি করা যেকোনো ব্যক্তিগত প্রেরন সারি ডিফল্টভাবে সিরিয়াল, কিন্তু পূর্বে আলোচনা করা মত একটি ঐচ্ছিক বৈশিষ্ট্য ব্যবহার করে সমসাময়িক হতে সেট করা যেতে পারে।
এখানে সিরিয়াল ধারণাটি উল্লেখ করা গুরুত্বপূর্ণ বনাম সমসাময়িক একটি নির্দিষ্ট সারি আলোচনা করার সময় শুধুমাত্র প্রাসঙ্গিক. সমস্ত সারি একে অপরের সাপেক্ষে সমসাময়িক .
অর্থাৎ, যদি আপনি মূল সারি থেকে একটি প্রাইভেট সিরিয়ালতে অ্যাসিঙ্ক্রোনাসভাবে কাজ পাঠান সারি, সেই কাজটি একসাথে সম্পন্ন হবে প্রধান সারির সাপেক্ষে। এবং যদি আপনি দুটি ভিন্ন সিরিয়াল সারি তৈরি করেন, এবং তারপর তাদের একটিতে ব্লক করার কাজ করেন, অন্য সারিটি প্রভাবিত হয় না৷
একাধিক সিরিয়াল সারির একত্রীকরণ প্রদর্শন করতে, আসুন এই উদাহরণটি নেওয়া যাক:
let serial1 = DispatchQueue(label: "com.besher.serial1")
let serial2 = DispatchQueue(label: "com.besher.serial2")
serial1.async {
for _ in 0..<5 { print("?") }
}
serial2.async {
for _ in 0..<5 { print("?") }
}
এখানে উভয় সারিই সিরিয়াল, কিন্তু ফলাফলগুলি এলোমেলো হয় কারণ তারা একে অপরের সাথে একযোগে কার্যকর করে। তারা প্রতিটি সিরিয়াল (বা সমসাময়িক) এই ফলাফলের উপর কোন প্রভাব ফেলে না। তাদের QoS স্তর নির্ধারণ করে কে করবে সাধারণত আগে শেষ করুন (অর্ডার নিশ্চিত নয়)।
যদি আমরা নিশ্চিত করতে চাই যে দ্বিতীয় লুপটি শুরু করার আগে প্রথম লুপটি শেষ হয়, আমরা কলার থেকে সিঙ্ক্রোনাসভাবে প্রথম কাজটি জমা দিতে পারি:
let serial1 = DispatchQueue(label: "com.besher.serial1")
let serial2 = DispatchQueue(label: "com.besher.serial2")
serial1.sync { // <---- we changed this to 'sync'
for _ in 0..<5 { print("?") }
}
// we don't get here until first loop terminates
serial2.async {
for _ in 0..<5 { print("?") }
}
এটি অগত্যা কাম্য নয়, কারণ আমরা এখন কলকারীকে ব্লক করছি যখন প্রথম লুপটি কার্যকর হচ্ছে৷
কলারকে ব্লক করা এড়াতে, আমরা উভয় কাজই অ্যাসিঙ্ক্রোনাসভাবে জমা দিতে পারি, কিন্তু একই সিরিয়াল সারি:
let serial = DispatchQueue(label: "com.besher.serial")
serial.async {
for _ in 0..<5 { print("?") }
}
serial.async {
for _ in 0..<5 { print("?") }
}
এখন কলার এর সাথে আমাদের কাজগুলি একযোগে সঞ্চালিত হয়৷ , তাদের অর্ডার অক্ষত রাখার সময়।
মনে রাখবেন যে যদি আমরা ঐচ্ছিক প্যারামিটারের মাধ্যমে আমাদের একক সারি একত্রিত করি, তাহলে আমরা প্রত্যাশিত ফলাফলে ফিরে যাব:
let concurrent = DispatchQueue(label: "com.besher.concurrent", attributes: .concurrent)
concurrent.async {
for _ in 0..<5 { print("?") }
}
concurrent.async {
for _ in 0..<5 { print("?") }
}
কখনও কখনও আপনি সিরিয়াল এক্সিকিউশনের সাথে সিঙ্ক্রোনাস এক্সিকিউশনকে বিভ্রান্ত করতে পারেন (অন্তত আমি করেছিলাম), কিন্তু সেগুলি খুব আলাদা জিনিস। উদাহরণস্বরূপ, আমাদের পূর্ববর্তী উদাহরণ থেকে লাইন 3-এ প্রথম প্রেরণ একটি sync
পরিবর্তন করার চেষ্টা করুন কল করুন:
let concurrent = DispatchQueue(label: "com.besher.concurrent", attributes: .concurrent)
concurrent.sync {
for _ in 0..<5 { print("?") }
}
concurrent.async {
for _ in 0..<5 { print("?") }
}
হঠাৎ করে, আমাদের ফলাফল নিখুঁত ক্রমে ফিরে এসেছে। কিন্তু এটি একটি সমবর্তী সারি, তাই কিভাবে ঘটতে পারে? sync
করেছে বিবৃতি কোনভাবে একটি সিরিয়াল সারিতে পরিণত?
উত্তর হল না!
এটি একটি বিট লুকোচুরি. কি হয়েছে যে আমরা async
এ পৌঁছাইনি প্রথম টাস্কটি কার্যকর না হওয়া পর্যন্ত কল করুন। সারি এখনও অনেক সমসাময়িক, কিন্তু কোডের এই জুম-ইন বিভাগের ভিতরে। এটা সিরিয়াল ছিল মনে হয়. এর কারণ হল আমরা কলারকে ব্লক করছি, এবং প্রথমটি শেষ না হওয়া পর্যন্ত পরবর্তী কাজে এগোচ্ছি না।
যদি আপনার অ্যাপের অন্য কোথাও অন্য সারিটি sync
চালানোর সময় একই সারিতে কাজ জমা দেওয়ার চেষ্টা করে বিবৃতি, যে কাজ হবে আমরা এখানে যা কিছু দৌড়াতে পেরেছি তার সাথে একযোগে চালান, কারণ এটি এখনও একটি সমবর্তী সারি।
কোনটি ব্যবহার করবেন?
সিরিয়াল সারিগুলি সিপিইউ অপ্টিমাইজেশান এবং ক্যাশিংয়ের সুবিধা নেয় এবং প্রসঙ্গ স্যুইচিং কমাতে সহায়তা করে।
অ্যাপল আপনার অ্যাপে প্রতি সাবসিস্টেমের জন্য একটি সিরিয়াল সারি দিয়ে শুরু করার পরামর্শ দেয়—যেমন নেটওয়ার্কিংয়ের জন্য একটি, ফাইল কম্প্রেশনের জন্য একটি, ইত্যাদি। প্রয়োজন দেখা দিলে, আপনি পরবর্তীতে setTarget পদ্ধতি বা ঐচ্ছিক টার্গেট ব্যবহার করে সাবসিস্টেম প্রতি সারিগুলির একটি শ্রেণিবিন্যাসে প্রসারিত করতে পারেন। সারি তৈরি করার সময় প্যারামিটার।
আপনি যদি পারফরম্যান্সের বাধার সম্মুখীন হন, তাহলে আপনার অ্যাপের কর্মক্ষমতা পরিমাপ করুন তারপর দেখুন সমসাময়িক সারি সাহায্য করে কিনা। যদি আপনি একটি পরিমাপযোগ্য সুবিধা দেখতে না পান, তাহলে সিরিয়াল সারিতে আটকে থাকা ভাল।
বিপত্তি
অগ্রাধিকার উল্টানো এবং পরিষেবার গুণমান
অগ্রাধিকার বিপর্যয় হল যখন একটি উচ্চ অগ্রাধিকারের কাজকে একটি নিম্ন অগ্রাধিকারের কাজ দ্বারা চালানো থেকে বাধা দেওয়া হয়, কার্যকরভাবে তাদের আপেক্ষিক অগ্রাধিকারগুলিকে উল্টে দেয়৷
এই পরিস্থিতি প্রায়ই ঘটে যখন একটি উচ্চ QoS সারি একটি নিম্ন QoS সারির সাথে একটি সংস্থান ভাগ করে এবং নিম্ন QoS সারিতে সেই সংস্থানটিতে একটি লক থাকে।
কিন্তু আমি একটি ভিন্ন দৃশ্যকল্প কভার করতে চাই যা আমাদের আলোচনার সাথে আরও প্রাসঙ্গিক—যখন আপনি একটি নিম্ন QoS সিরিয়াল সারিতে কাজ জমা দেন, তারপর সেই একই সারিতে একটি উচ্চ QoS টাস্ক জমা দেন। এই দৃশ্যটিও অগ্রাধিকারের বিপরীতে পরিণত হয়, কারণ উচ্চ QoS টাস্কের জন্য নিম্ন QoS টাস্কগুলি শেষ হওয়ার জন্য অপেক্ষা করতে হয়।
GCD সাময়িকভাবে সারির QoS বাড়িয়ে অগ্রাধিকার বিপরীত সমাধান করে যাতে নিম্ন অগ্রাধিকারের কাজগুলি রয়েছে যা আপনার উচ্চ অগ্রাধিকারের টাস্কের 'আগে' বা ব্লক করে।
এটা অনেকটা গাড়িসামনে আটকে থাকার মতো এর একটি অসুস্থ ও আহতদের পরিবহনের গাড়ি. হঠাৎ করে তাদের লাল আলো পার হওয়ার অনুমতি দেওয়া হয় যাতে অ্যাম্বুলেন্সটি চলতে পারে (বাস্তবে গাড়িগুলি পাশে চলে যায়, কিন্তু একটি সরু (ক্রমিক) রাস্তা বা অন্য কিছু কল্পনা করুন, আপনি পয়েন্ট পাবেন :-P)
ইনভার্সন সমস্যাটি বোঝাতে, এই কোড দিয়ে শুরু করা যাক:
enum Color: String {
case blue = "?"
case white = "⚪️"
}
func output(color: Color, times: Int) {
for _ in 1...times {
print(color.rawValue)
}
}
let starterQueue = DispatchQueue(label: "com.besher.starter", qos: .userInteractive)
let utilityQueue = DispatchQueue(label: "com.besher.utility", qos: .utility)
let backgroundQueue = DispatchQueue(label: "com.besher.background", qos: .background)
let count = 10
starterQueue.async {
backgroundQueue.async {
output(color: .white, times: count)
}
backgroundQueue.async {
output(color: .white, times: count)
}
utilityQueue.async {
output(color: .blue, times: count)
}
utilityQueue.async {
output(color: .blue, times: count)
}
// next statement goes here
}
আমরা একটি স্টার্টার সারি তৈরি করি (যেখানে আমরা থেকে কাজগুলি জমা করি ), পাশাপাশি বিভিন্ন QoS সহ দুটি সারি। তারপরে আমরা এই দুটি সারির প্রতিটিতে কাজগুলি প্রেরণ করি, প্রতিটি টাস্ক একটি নির্দিষ্ট রঙের সমান সংখ্যক বৃত্ত মুদ্রণ করে (উপযোগিতা সারি নীল, পটভূমি সাদা।)
যেহেতু এই কাজগুলি অ্যাসিঙ্ক্রোনাসভাবে জমা দেওয়া হয়েছে, আপনি যতবার অ্যাপটি চালাবেন, আপনি কিছুটা ভিন্ন ফলাফল দেখতে যাচ্ছেন। যাইহোক, আপনি যেমনটি আশা করবেন, নিম্ন QoS (পটভূমি) সহ সারিটি প্রায় সবসময় শেষ হয়। প্রকৃতপক্ষে, শেষ 10-15টি চেনাশোনা সাধারণত সব সাদা।
কিন্তু আমরা যখন একটি সিঙ্ক জমা দিই তখন কী হয় তা দেখুন শেষ অ্যাসিঙ্ক স্টেটমেন্টের পরে ব্যাকগ্রাউন্ড কিউতে টাস্ক। এমনকি আপনাকে sync
এর ভিতরে কিছু প্রিন্ট করতে হবে না বিবৃতি, শুধুমাত্র এই লাইন যোগ করা যথেষ্ট:
// add this after the last async statement,
// still inside starterQueue.async
backgroundQueue.sync {}
কনসোলে ফলাফল উল্টে গেছে! এখন, উচ্চ অগ্রাধিকার সারি (ইউটিলিটি) সর্বদা শেষ হয় এবং শেষ 10-15টি চেনাশোনা নীল হয়।
কেন এটি ঘটে তা বোঝার জন্য, আমাদের এই সত্যটি পুনরায় দেখতে হবে যে কলার থ্রেডে সিঙ্ক্রোনাস কাজ চালানো হয় (যদি না আপনি মূল সারিতে জমা দিচ্ছেন।)
আমাদের উপরের উদাহরণে, কলকারীর (স্টার্টার সারি) শীর্ষে রয়েছে QoS (ইউজার ইন্টারেক্টিভ।) তাই, এটি আপাতদৃষ্টিতে নিরীহ sync
কাজটি শুধুমাত্র স্টার্টার সারিকে ব্লক করছে না, এটি স্টার্টারের উচ্চ QoS থ্রেডেও চলছে। তাই টাস্কটি উচ্চ QoS এর সাথে চলে, কিন্তু একই ব্যাকগ্রাউন্ড সারিতে এর আগে আরও দুটি কাজ আছে যেগুলির পটভূমি আছে QoS অগ্রাধিকার বিপরীত শনাক্ত করা হয়েছে!
যেমন প্রত্যাশিত, GCD সাময়িকভাবে উচ্চ QoS টাস্কের সাথে সামঞ্জস্যপূর্ণ করার জন্য সমগ্র সারির QoS উত্থাপন করে এই উল্টোকরণের সমাধান করে। ফলস্বরূপ, ব্যাকগ্রাউন্ড সারিতে থাকা সমস্ত কাজ শেষ হয় ইন্টারেক্টিভ ব্যবহারকারী এ QoS, যা ইউটিলিটি থেকে বেশি QoS আর সেই কারণেই ইউটিলিটি টাস্ক শেষ হয়!
সাইড-নোট:আপনি যদি সেই উদাহরণ থেকে স্টার্টার সারিটি সরিয়ে দেন এবং পরিবর্তে মূল সারি থেকে জমা দেন, তাহলে আপনি একই রকম ফলাফল পাবেন, কারণ মূল সারিতেও ব্যবহারকারী ইন্টারেক্টিভ আছে QoS।
এই উদাহরণে অগ্রাধিকার বিপর্যয় এড়াতে, আমাদের sync
দিয়ে স্টার্টার সারি ব্লক করা এড়াতে হবে বিবৃতি async
ব্যবহার করা হচ্ছে সেই সমস্যার সমাধান হবে।
যদিও এটি সর্বদা আদর্শ নয়, আপনি প্রাইভেট সারি তৈরি করার সময় বা গ্লোবাল কনকারেন্ট কিউতে পাঠানোর সময় ডিফল্ট QoS এ আটকে রেখে অগ্রাধিকার বিপর্যয়গুলি কমিয়ে আনতে পারেন৷
থ্রেড বিস্ফোরণ
আপনি যখন সমসাময়িক সারি ব্যবহার করেন, আপনি সতর্ক না হলে থ্রেড বিস্ফোরণের ঝুঁকি চালান। এটি ঘটতে পারে যখন আপনি একটি সমসাময়িক সারিতে কাজ জমা দেওয়ার চেষ্টা করেন যা বর্তমানে ব্লক করা আছে (উদাহরণস্বরূপ একটি সেমাফোর, সিঙ্ক বা অন্য কোনো উপায়ে।) আপনার কাজগুলি হবে চালান, কিন্তু সিস্টেম সম্ভবত এই নতুন কাজগুলিকে মিটমাট করার জন্য নতুন থ্রেড স্পিন করবে এবং থ্রেডগুলি সস্তা নয়৷
সম্ভবত এই কারণেই অ্যাপল আপনার অ্যাপে সাবসিস্টেম প্রতি একটি সিরিয়াল সারি দিয়ে শুরু করার পরামর্শ দেয়, কারণ প্রতিটি সিরিয়াল সারি শুধুমাত্র একটি থ্রেড ব্যবহার করতে পারে। মনে রাখবেন যে সিরিয়াল সারিগুলি সম্পর্কে সমসাময়িক প্রতিঅন্য সারি, তাই আপনি যখন আপনার কাজকে একটি সারিতে অফলোড করেন তখনও আপনি একটি পারফরম্যান্স বেনিফিট পান, এমনকি এটি সমসাময়িক না হলেও৷
জাতির অবস্থা
সুইফ্ট অ্যারে, ডিকশনারী, স্ট্রাকট এবং অন্যান্য মান প্রকার ডিফল্টরূপে থ্রেড-নিরাপদ নয়। উদাহরণস্বরূপ, যখন আপনার একাধিক থ্রেড অ্যাক্সেস এবং পরিবর্তন করার চেষ্টা করছে একই অ্যারে, আপনি সমস্যায় পড়তে শুরু করবেন।
পাঠক-লেখকদের সমস্যার বিভিন্ন সমাধান আছে, যেমন লক বা সেমাফোর ব্যবহার করা। কিন্তু আমি এখানে যে প্রাসঙ্গিক সমাধান নিয়ে আলোচনা করতে চাই তা হল একটি বিচ্ছিন্নতা সারির ব্যবহার।
ধরা যাক আমাদের কাছে পূর্ণসংখ্যার একটি অ্যারে রয়েছে এবং আমরা অ্যাসিঙ্ক্রোনাস কাজ জমা দিতে চাই যা এই অ্যারের উল্লেখ করে। যতক্ষণ না আমাদের কাজ শুধুমাত্র পড়ে অ্যারে এবং এটি পরিবর্তন না, আমরা নিরাপদ. কিন্তু যত তাড়াতাড়ি আমরা আমাদের অ্যাসিঙ্ক্রোনাস টাস্কগুলির একটিতে অ্যারেটি পরিবর্তন করার চেষ্টা করব, আমরা আমাদের অ্যাপে অস্থিরতা প্রবর্তন করব৷
এটি একটি জটিল সমস্যা কারণ আপনার অ্যাপটি 10 বার সমস্যা ছাড়াই চলতে পারে এবং তারপর এটি 11তম বার ক্র্যাশ হয়ে যায়। এই পরিস্থিতির জন্য একটি খুব সহজ টুল হল এক্সকোডে থ্রেড স্যানিটাইজার। এই বিকল্পটি সক্ষম করা আপনাকে আপনার অ্যাপে সম্ভাব্য রেসের অবস্থা সনাক্ত করতে সহায়তা করবে৷
সমস্যাটি প্রদর্শনের জন্য, আসুন এই (স্বীকৃতভাবে অনুপ্রাণিত) উদাহরণটি নেওয়া যাক:
class ViewController: UIViewController {
let concurrent = DispatchQueue(label: "com.besher.concurrent", attributes: .concurrent)
var array = [1,2,3,4,5]
override func viewDidLoad() {
for _ in 0...1 {
race()
}
}
func race() {
concurrent.async {
for i in self.array { // read access
print(i)
}
}
concurrent.async {
for i in 0..<10 {
self.array.append(i) // write access
}
}
}
}
async
এর মধ্যে একটি কার্যগুলি মান যুক্ত করে অ্যারেকে সংশোধন করছে। আপনি যদি এটি আপনার সিমুলেটরে চালানোর চেষ্টা করেন তবে আপনি ক্র্যাশ নাও করতে পারেন। কিন্তু এটি পর্যাপ্ত বার চালান (বা লাইন 7 এ লুপ ফ্রিকোয়েন্সি বাড়ান), এবং আপনি শেষ পর্যন্ত ক্র্যাশ হবেন। আপনি যদি থ্রেড স্যানিটাইজার সক্ষম করেন, আপনি প্রতিবার অ্যাপটি চালানোর সময় একটি সতর্কতা পাবেন।
এই রেসের অবস্থার সাথে মোকাবিলা করার জন্য, আমরা একটি বিচ্ছিন্নতা সারি যোগ করতে যাচ্ছি যা বাধা পতাকা ব্যবহার করে। এই ফ্ল্যাগটি সারিতে থাকা যেকোনও অসামান্য কাজগুলিকে শেষ করার অনুমতি দেয়, কিন্তু বাধার কাজটি সম্পূর্ণ না হওয়া পর্যন্ত অন্য কোনও কাজকে কার্যকর করা থেকে ব্লক করে৷
একজন দারোয়ান পাবলিক বিশ্রামাগার পরিষ্কার করার মতো বাধার কথা চিন্তা করুন (ভাগ করা সম্পদ) বিশ্রামাগারের ভিতরে একাধিক (সমসাময়িক) স্টল রয়েছে যা লোকেরা ব্যবহার করতে পারে।
আগমনের পরে, দারোয়ান একটি পরিষ্কারের চিহ্ন (বাধা) স্থাপন করে যে কোনও নতুনদের পরিস্কার না হওয়া পর্যন্ত প্রবেশ করতে বাধা দেয়, তবে ভিতরে থাকা সমস্ত লোক তাদের ব্যবসা শেষ না করা পর্যন্ত দারোয়ান পরিষ্কার করা শুরু করেন না। তারা সবাই চলে গেলে, দারোয়ান বিচ্ছিন্নভাবে পাবলিক বিশ্রামাগার পরিষ্কার করতে এগিয়ে যান।
শেষ পর্যন্ত সম্পন্ন হলে, দারোয়ান সাইন (বাধা) সরিয়ে দেয় যাতে বাইরে সারিবদ্ধ লোকেরা অবশেষে প্রবেশ করতে পারে।
কোডে এটি দেখতে কেমন তা এখানে:
class ViewController: UIViewController {
let concurrent = DispatchQueue(label: "com.besher.concurrent", attributes: .concurrent)
let isolation = DispatchQueue(label: "com.besher.isolation", attributes: .concurrent)
private var _array = [1,2,3,4,5]
var threadSafeArray: [Int] {
get {
return isolation.sync {
_array
}
}
set {
isolation.async(flags: .barrier) {
self._array = newValue
}
}
}
override func viewDidLoad() {
for _ in 0...15 {
race()
}
}
func race() {
concurrent.async {
for i in self.threadSafeArray {
print(i)
}
}
concurrent.async {
for i in 0..<10 {
self.threadSafeArray.append(i)
}
}
}
}
আমরা একটি নতুন বিচ্ছিন্নতা সারি যোগ করেছি, এবং একটি গেটার এবং সেটার ব্যবহার করে প্রাইভেট অ্যারেতে সীমাবদ্ধ অ্যাক্সেস করেছি যা অ্যারেটি পরিবর্তন করার সময় একটি বাধা সৃষ্টি করবে৷
গেটারকে sync
হতে হবে সরাসরি একটি মান ফেরত দেওয়ার জন্য। সেটার async
হতে পারে , যেহেতু লেখার সময় আমাদের কলারকে ব্লক করার দরকার নেই।
আমরা রেস কন্ডিশনের সমাধান করার জন্য কোনো বাধা ছাড়াই সিরিয়াল কিউ ব্যবহার করতে পারতাম, কিন্তু তারপর আমরা অ্যারেতে একযোগে পড়ার অ্যাক্সেস পাওয়ার সুবিধা হারাবো। সম্ভবত এটি আপনার ক্ষেত্রে অর্থপূর্ণ, আপনি সিদ্ধান্ত নিতে পারেন।
উপসংহার
এই পর্যন্ত পড়ার জন্য আপনাকে অনেক ধন্যবাদ! আমি আশা করি আপনি এই নিবন্ধ থেকে নতুন কিছু শিখেছি. আমি আপনাকে একটি সারসংক্ষেপ এবং কিছু সাধারণ পরামর্শ দিয়ে যাব:
সারাংশ
- সারি সবসময় শুরু FIFO অর্ডারে তাদের কাজগুলি
- সারি সবসময় অন্যান্য এর সাথে সমসাময়িক হয় সারি
- সিঙ্ক বনাম অসিঙ্ক উত্স উদ্বেগ
- সিরিয়াল বনাম সমসাময়িক গন্তব্য নিয়ে চিন্তা করে
- সিঙ্ক হল 'ব্লকিং' এর সমার্থক
- অসিঙ্ক অবিলম্বে কলারকে নিয়ন্ত্রণ ফিরিয়ে দেয়
- ক্রমিক একটি একক থ্রেড ব্যবহার করে, এবং মৃত্যুদন্ড কার্যকর করার গ্যারান্টি দেয়
- সমসাময়িক একাধিক-থ্রেড ব্যবহার করে, এবং থ্রেড বিস্ফোরণের ঝুঁকি রাখে
- আপনার ডিজাইন চক্রের প্রথম দিকে একত্রিতার কথা চিন্তা করুন
- সিঙ্ক্রোনাস কোড সম্পর্কে যুক্তি এবং ডিবাগ করা সহজ
- যদি সম্ভব হয় বিশ্বব্যাপী সমসাময়িক সারির উপর নির্ভর করা এড়িয়ে চলুন
- সাবসিস্টেম প্রতি একটি সিরিয়াল সারি দিয়ে শুরু করার কথা বিবেচনা করুন
- যদি আপনি একটি পরিমাপযোগ্য দেখতে পান তবেই সমবর্তী সারিতে স্যুইচ করুন কর্মক্ষমতা সুবিধা
আমি সুইফ্ট কনকারেন্সি ইশতেহারের রূপকটি পছন্দ করি যার একটি 'সমগরে ধারাবাহিককরণের দ্বীপ' রয়েছে। এই অনুভূতিটি ম্যাট ডাইপহাউসের এই টুইটটিতেও ভাগ করা হয়েছে:
সমবর্তী কোড লেখার রহস্য হল এর বেশিরভাগ সিরিয়াল তৈরি করা। একটি ছোট, বাইরের স্তরে একত্রিতকরণ সীমাবদ্ধ করুন। (সিরিয়াল কোর, সমবর্তী শেল।)
— ম্যাট ডিহাউস (@mdiep) ডিসেম্বর 18, 2019
যেমন 5টি বৈশিষ্ট্য পরিচালনা করার জন্য একটি লক ব্যবহার করার পরিবর্তে, একটি নতুন টাইপ তৈরি করুন যা সেগুলিকে মোড়ানো এবং লকের ভিতরে একটি একক সম্পত্তি ব্যবহার করুন৷
যখন আপনি সেই দর্শনকে মাথায় রেখে একযোগে প্রয়োগ করেন, আমি মনে করি এটি আপনাকে সমকালীন কোড অর্জনে সহায়তা করবে যা কলব্যাকের জগাখিচুড়িতে হারিয়ে না গিয়ে যুক্তিযুক্ত হতে পারে।
আপনার যদি কোনো প্রশ্ন বা মন্তব্য থাকে, তাহলে নির্দ্বিধায় আমার সাথে Twitter এ যোগাযোগ করুন
বেশের আল মালেহ
আনস্প্ল্যাশে Onur K-এর কভার ফটো
সঙ্গী অ্যাপটি এখানে ডাউনলোড করুন:
almaleh/DispatcherCompanion অ্যাপ একযোগে আমার নিবন্ধে। GitHub-এ একটি অ্যাকাউন্ট তৈরি করে almaleh/Dispatcher বিকাশে অবদান রাখুন। almalehGitHubআমার অন্যান্য নিবন্ধগুলি দেখুন:
ফায়ারওয়ার্কস—একটি ভিজ্যুয়াল পার্টিকেল এডিটর সুইফট জেনারেট সুইফট কোড অন দ্য ফ্লাই ম্যাকওএস এবং আইওএস-এর জন্য যখন আপনি আপনার কণার প্রভাবগুলি ডিজাইন করেন এবং পুনরাবৃত্তি করেন বেশের আল মালেহ ত্রুটিহীন iOS আপনার (সর্বদা) প্রয়োজন নেই [দুর্বল আত্ম] এই নিবন্ধে, আমরা দুর্বল আত্ম সম্পর্কে কথা বলব চক্র ধরে রাখা এড়াতে সুইফ্ট বন্ধের অভ্যন্তরে এবং এমন ক্ষেত্রে অন্বেষণ করুন যেখানে নিজেকে দুর্বলভাবে ক্যাপচার করার প্রয়োজন হতে পারে বা নাও হতে পারে। Besher Al MalehFlawless iOSFurther reading:
IntroductionExplains how to implement concurrent code paths in an application.Concurrent Programming:APIs and Challenges · objc.ioobjc.io publishes books on advanced techniques and practices for iOS and OS X development Florian Kugler Low-Level Concurrency APIs · objc.ioobjc.io publishes books on advanced techniques and practices for iOS and OS X development Daniel Eggerthttps://khanlou.com/2016/04/the-GCD-handbook/
Concurrent vs serial queues in GCDI’m struggling to fully understand the concurrent and serial queues in GCD. I have some issues and hoping someone can answer me clearly and at the point.I’m reading that serial queues are created... Bogdan AlexandruStack Overflow