আমি sync.Cond
টাইপ এবং কেস ব্যবহার এবং কখন এটি ব্যবহার করতে হবে তা নিয়ে আলোচনা করতে চাই।
sync.Cond
কি? গো প্রোগ্রামিং ল্যাঙ্গুয়েজে, sync.Cond
হল sync
প্যাকেজে সংজ্ঞায়িত একটি টাইপ যা একটি শর্ত ভেরিয়েবলের প্রতিনিধিত্ব করে। কন্ডিশন ভেরিয়েবল হল সিঙ্ক্রোনাইজেশন প্রাইমিটিভ যা গোরুটিন সমন্বয় করার জন্য ব্যবহার করা হয় যাতে তারা এগিয়ে যাওয়ার আগে একটি নির্দিষ্ট শর্ত সত্য হওয়ার জন্য অপেক্ষা করতে দেয়।
sync.Cond
প্রকার কন্ডিশন ভেরিয়েবল তৈরি এবং পরিচালনা করার একটি উপায় প্রদান করে। এটির তিনটি প্রধান পদ্ধতি রয়েছে:
Wait()
: এই পদ্ধতিটি কলিং গরউটিনকে অপেক্ষা করতে দেয় যতক্ষণ না অন্য একটি গোরুটিন কন্ডিশন ভেরিয়েবলকে সংকেত দেয়। যখন গোরুটিন Wait()
কে কল করে, তখন এটি সংশ্লিষ্ট লকটি প্রকাশ করে এবং একই sync.Cond
ভেরিয়েবলে অন্য গুরুটিন Signal()
বা Broadcast()
কল না করা পর্যন্ত এক্সিকিউশন স্থগিত করে।
Signal()
: এই পদ্ধতিটি কন্ডিশন ভেরিয়েবলের উপর অপেক্ষারত একটি গোরুটিনকে জাগিয়ে তোলে। যদি একাধিক গোরুটিন অপেক্ষা করে থাকে, তবে তাদের মধ্যে শুধুমাত্র একটি জাগ্রত হয়। কোন গুরুটিন জাগ্রত হয় তার পছন্দ নির্বিচারে এবং নিশ্চিত নয়।
Broadcast()
: এই পদ্ধতিটি কন্ডিশন ভেরিয়েবলের জন্য অপেক্ষা করা সমস্ত গোরুটিনকে জাগিয়ে তোলে। যখন Broadcast()
কল করা হয়, তখন সমস্ত অপেক্ষমাণ গোরুটিন জাগ্রত হয় এবং এগিয়ে যেতে পারে।
নোট করুন যে কন্ডিশন ভেরিয়েবলে অ্যাক্সেস সিঙ্ক্রোনাইজ করতে sync.Cond
একটি সংশ্লিষ্ট sync.Mutex
প্রয়োজন।
sync.Cond
ব্যবহার করে, আপনি Go-তে আরও নিয়ন্ত্রিত এবং সিঙ্ক্রোনাইজড সমসাময়িক প্রোগ্রামিংয়ের অনুমতি দিয়ে নির্দিষ্ট শর্তের উপর ভিত্তি করে গরউটিনগুলির সম্পাদনের সমন্বয় করতে পারেন।
sync.Cond
সাধারণত এমন পরিস্থিতিতে ব্যবহৃত হয় যেখানে goroutinesকে নির্দিষ্ট অবস্থার উপর ভিত্তি করে একে অপরের সাথে সমন্বয় ও যোগাযোগ করতে হয়। আসুন sync.Cond
এর জন্য সাধারণ ব্যবহারের ক্ষেত্রে বিবেচনা করা যাক।
sync.Cond
একাধিক গরউটিনের সম্পাদন সিঙ্ক্রোনাইজ করতে ব্যবহার করা যেতে পারে। উদাহরণস্বরূপ, আপনার বিভিন্ন গোরুটিন থাকতে পারে যা এগিয়ে যাওয়ার আগে একটি নির্দিষ্ট শর্ত সন্তুষ্ট হওয়ার জন্য অপেক্ষা করতে হবে। ওয়েটিং গোরুটিন কল করতে পারে cond.Wait()
, এবং সিগন্যালিং গুরুটিন cond.Signal()
বা cond.Broadcast()
কল করতে পারে যখন শর্ত পূরণ হয় তখন অপেক্ষমাণ গোরুটিনগুলিকে জাগিয়ে তুলতে পারে৷
package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup var mu sync.Mutex cond := sync.NewCond(&mu) wg.Add(2) go func() { fmt.Println("Goroutine 1 is started") defer wg.Done() cond.L.Lock() defer cond.L.Unlock() fmt.Println("Goroutine 1 is waiting for condition") cond.Wait() fmt.Println("Goroutine 1 met the condition") fmt.Println("Goroutine 1 is done") }() go func() { fmt.Println("Goroutine 2 is started") defer wg.Done() time.Sleep(5 * time.Second) // Simulating some work cond.L.Lock() defer cond.L.Unlock() fmt.Println("Goroutine 2 is signaling condition") cond.Signal() fmt.Println("Goroutine 2 completed signaling") fmt.Println("Goroutine 2 is done") }() wg.Wait() }
এই উদাহরণে, আমাদের দুটি গোরুটিন আছে। প্রথম goroutine cond.Wait()
ব্যবহার করে একটি শর্তের জন্য অপেক্ষা করে, যখন দ্বিতীয় goroutine cond.Signal()
ব্যবহার করে শর্তের সংকেত দেয়।
যখন প্রোগ্রামটি কার্যকর হয়, প্রথম গোরুটিন লকটি অর্জন করে এবং তারপরে cond.Wait()
কল করে। যেহেতু শর্তটি এখনও পূরণ করা হয়নি, প্রথম গোরুটিন লকটি প্রকাশ করে এবং এর সম্পাদন স্থগিত করে।
এদিকে, দ্বিতীয় গোরুটিন পাঁচ সেকেন্ডের জন্য ঘুমায়, কিছু কাজ অনুকরণ করে। এটি লকটি অর্জন করে এবং তারপর cond.Signal()
কল করে। এটি ওয়েটিং গোরুটিনকে জাগিয়ে তোলে, যা তারপর লকটি অর্জন করে এবং কার্যকর করে।
sync.Cond
এর ব্যবহার নিশ্চিত করে যে প্রথম goroutine অপেক্ষা করে যতক্ষণ না দ্বিতীয় goroutine শর্তটি সংকেত দেয়, যাতে দুটি goroutine-এর মধ্যে সমন্বয় ও সমন্বয় করা যায়।
sync.Cond
প্রযোজক-ভোক্তা সমস্যা সমাধানে উপযোগী হতে পারে, একটি ক্লাসিক সিঙ্ক্রোনাইজেশন সমস্যা যার মধ্যে দুটি ধরণের প্রক্রিয়া, প্রযোজক এবং ভোক্তা জড়িত, যেগুলি একটি সাধারণ নির্দিষ্ট-আকারের বাফার বা সারি ভাগ করে। প্রযোজক গোরুটিনগুলি ব্যবহার করতে পারে cond.Signal()
বা cond.Broadcast()
ব্যবহার করে গ্রাহকদের গুরুটিনগুলিকে অবহিত করার জন্য যখন নতুন ডেটা ব্যবহারের জন্য উপলব্ধ হয়৷
package main import ( "fmt" "sync" "time" ) const MaxMessageChannelSize = 5 func main() { var wg sync.WaitGroup var mu sync.Mutex cond := sync.NewCond(&mu) messageChannel := NewMessageChannel(MaxMessageChannelSize) producer := NewProducer(cond, messageChannel) consumer := NewConsumer(cond, messageChannel) wg.Add(2) go func() { defer wg.Done() for i := range 10 { producer.Produce(fmt.Sprintf("Message %d", i)) } }() go func() { defer wg.Done() for range 10 { consumer.Consume() } }() wg.Wait() } type MessageChannel struct { maxBufferSize int buffer []string } func NewMessageChannel(size int) *MessageChannel { return &MessageChannel{ maxBufferSize: size, buffer: make([]string, 0, size), } } func (mc *MessageChannel) IsEmpty() bool { return len(mc.buffer) == 0 } func (mc *MessageChannel) IsFull() bool { return len(mc.buffer) == mc.maxBufferSize } func (mc *MessageChannel) Add(message string) { mc.buffer = append(mc.buffer, message) } func (mc *MessageChannel) Get() string { message := mc.buffer[0] mc.buffer = mc.buffer[1:] return message } type Producer struct { cond *sync.Cond messageChannel *MessageChannel } func NewProducer(cond *sync.Cond, messageChannel *MessageChannel) *Producer { return &Producer{ cond: cond, messageChannel: messageChannel, } } func (p *Producer) Produce(message string) { time.Sleep(500 * time.Millisecond) // Simulating some work p.cond.L.Lock() defer p.cond.L.Unlock() for p.messageChannel.IsFull() { fmt.Println("Producer is waiting because the message channel is full") p.cond.Wait() } p.messageChannel.Add(message) fmt.Println("Producer produced the message:", message) p.cond.Signal() } type Consumer struct { id int cond *sync.Cond messageChannel *MessageChannel } func NewConsumer(cond *sync.Cond, messageChannel *MessageChannel) *Consumer { return &Consumer{ cond: cond, messageChannel: messageChannel, } } func (c *Consumer) Consume() { time.Sleep(1 * time.Second) // Simulating some work c.cond.L.Lock() defer c.cond.L.Unlock() for c.messageChannel.IsEmpty() { fmt.Println("Consumer is waiting because the message channel is empty") c.cond.Wait() } message := c.messageChannel.Get() fmt.Println("Consumer consumed the message:", message) c.cond.Signal() }
এই উদাহরণে, আমাদের কাছে একটি প্রযোজক গোরুটিন রয়েছে যা বার্তাগুলি তৈরি করে এবং সেগুলিকে বার্তা চ্যানেলে যুক্ত করে এবং একটি ভোক্তা গোরুটিন যা বার্তাগুলি গ্রহণ করে৷ বার্তা চ্যানেলের সর্বোচ্চ আকার MaxMessageChannelSize
দ্বারা সংজ্ঞায়িত করা হয়েছে।
প্রযোজক গোরুটিন বার্তা চ্যানেলে বার্তা যোগ করে এবং নতুন ডেটা উপলব্ধ হলে ভোক্তা গোরুটিনকে অবহিত করতে cond.Signal()
ব্যবহার করে। বার্তা চ্যানেলটি পূর্ণ হলে, প্রযোজক গোরুটিন cond.Wait()
ব্যবহার করে অপেক্ষা করে যতক্ষণ না ভোক্তা কিছু ডেটা ব্যবহার করে এবং বার্তা চ্যানেলে স্থান খালি করে না।
একইভাবে, ভোক্তা গোরুটিন বার্তা চ্যানেল থেকে বার্তা গ্রহণ করে এবং বার্তা চ্যানেলে স্থান পাওয়া গেলে প্রযোজক গোরুটিনকে অবহিত করতে cond.Signal()
ব্যবহার করে। এটি খালি থাকলে, কনজিউমার গোরুটিন cond.Wait()
ব্যবহার করে অপেক্ষা করে যতক্ষণ না প্রযোজক কিছু ডেটা তৈরি করে এবং এটি বার্তা চ্যানেলে যোগ করে।
এখানে, sync.Cond
প্রযোজক এবং ভোক্তা গোরুটিনের মধ্যে সমন্বয় এবং সমন্বয় সাধনের অনুমতি দেয়। এটি নিশ্চিত করে যে বার্তা চ্যানেলটি খালি হলে ভোক্তা অপেক্ষা করে এবং প্রযোজক অপেক্ষা করে যখন এটি পূর্ণ হয়, যার ফলে প্রযোজক-ভোক্তা সমস্যা সমাধান করা হয়।
ধরুন একাধিক গোরুটিনের একটি শেয়ার্ড রিসোর্সে একচেটিয়া অ্যাক্সেস প্রয়োজন। sync.Cond
অ্যাক্সেস সমন্বয় করতে ব্যবহার করা যেতে পারে। উদাহরণস্বরূপ, কর্মী গোরুটিনের একটি পুল প্রক্রিয়াকরণ শুরু করার আগে নির্দিষ্ট সংখ্যক সংস্থান উপলব্ধ না হওয়া পর্যন্ত অপেক্ষা করতে হতে পারে। goroutines cond.Wait()
ব্যবহার করে কন্ডিশন ভেরিয়েবলের উপর অপেক্ষা করতে পারে এবং cond.Signal()
বা cond.Broadcast()
ব্যবহার করে রিসোর্স রিলিজ করার বিষয়ে অবহিত করতে পারে।
package main import ( "fmt" "sync" "time" ) const MaxResources = 3 func main() { var wg sync.WaitGroup var mu sync.Mutex cond := sync.NewCond(&mu) resourceProvider := NewResourceProvider(cond, MaxResources) wg.Add(10) for i := range 10 { go func(workerID int) { defer wg.Done() worker := NewWorker(workerID, cond, resourceProvider) worker.Run() }(i) } wg.Wait() } type ResourceProvider struct { maxResources int availableResources int cond *sync.Cond } func NewResourceProvider(cond *sync.Cond, maxResources int) *ResourceProvider { return &ResourceProvider{ cond: cond, availableResources: maxResources, } } func (rp *ResourceProvider) AvailableResources() int { return rp.availableResources } func (rp *ResourceProvider) AcquireResoirce() { rp.availableResources-- } func (rp *ResourceProvider) ReleaseResource() { rp.availableResources++ } type Worker struct { id int cond *sync.Cond rp *ResourceProvider } func NewWorker(workerID int, cond *sync.Cond, rp *ResourceProvider) *Worker { return &Worker{ id: workerID, cond: cond, rp: rp, } } func (w *Worker) Run() { w.cond.L.Lock() for w.rp.AvailableResources() == 0 { fmt.Printf("Worker %d is waiting for resources\n", w.id) w.cond.Wait() } w.rp.AcquireResoirce() fmt.Printf("Worker %d acquired resource. Remaining resources: %d\n", w.id, w.rp.AvailableResources()) w.cond.L.Unlock() time.Sleep(1 * time.Second) // Simulating work w.cond.L.Lock() defer w.cond.L.Unlock() w.rp.ReleaseResource() fmt.Printf("Worker %d released resource. Remaining resources: %d\n", w.id, w.rp.AvailableResources()) w.cond.Signal() }
এই উদাহরণে, আমাদের একাধিক কর্মী গোরুটিন রয়েছে যেগুলির সীমিত সংস্থানগুলিতে একচেটিয়া অ্যাক্সেস প্রয়োজন৷ কর্মী গোরুটিন অন্যান্য কর্মীদের সাথে সমন্বয় করার জন্য cond.Signal()
ব্যবহার করে সম্পদ অর্জন করে এবং ছেড়ে দেয়। যদি কোন সংস্থান উপলব্ধ না হয়, কর্মী গোরুটিনরা using cond.Wait()
অপেক্ষা করে যতক্ষণ না অন্য গুরুটিন সংস্থানটি প্রকাশ করে।
এই উদাহরণে, sync.Cond
কর্মী গোরুটিনগুলির মধ্যে সমন্বয় এবং সমন্বয়ের জন্য অনুমতি দেয়, নিশ্চিত করে যে কর্মী গোরুটিনগুলি অপেক্ষা করে যখন কোনও সংস্থান উপলব্ধ না হয়, যার ফলে কার্যকরভাবে সংস্থান অ্যাক্সেস সিঙ্ক্রোনাইজ করা হয়।
sync.Cond
নির্দিষ্ট ইভেন্ট বা সিস্টেমের পরিবর্তন সম্পর্কে goroutines সূচিত করতে ব্যবহার করা যেতে পারে। উদাহরণস্বরূপ, আপনি একটি নির্দিষ্ট ইভেন্টের জন্য অপেক্ষা করতে গরউটিন থাকতে পারেন। ইভেন্টটি ঘটলে, সিগন্যালিং গোরুটিন cond.Signal()
বা cond.Broadcast()
ব্যবহার করে অপেক্ষমাণ গোরুটিনগুলিকে জাগিয়ে তুলতে পারে এবং তাদের ইভেন্ট পরিচালনা করার অনুমতি দেয়৷
package main import ( "fmt" "sync" "time" ) const maxWorkersCount = 10 func main() { var counter int32 var wg sync.WaitGroup var mu sync.Mutex cond := sync.NewCond(&mu) wg.Add(maxWorkersCount) for i := range maxWorkersCount { go func(workerID int) { defer wg.Done() fmt.Printf("Worker %d performing work\n", workerID) time.Sleep(1 * time.Second) // Simulate work cond.L.Lock() defer cond.L.Unlock() counter++ if counter == maxWorkersCount { fmt.Println("All workers have reached the barrier") cond.Broadcast() } else { fmt.Printf("Worker %d is waiting at the barrier\n", workerID) cond.Wait() } fmt.Printf("Worker %d passed the barrier\n", workerID) }(i) } wg.Wait() }
এখানে, আমাদের একাধিক কর্মী গোরুটিন রয়েছে যা কাজ সম্পাদন করে এবং একটি বাধা বিন্দুতে সিঙ্ক্রোনাইজ করে। কর্মী গোরুটিনগুলি একটি কাউন্টার বৃদ্ধি করে এবং তারপরে বাধার কাছে অপেক্ষা করে বা cond.Wait()
এবং cond.Broadcast()
ব্যবহার করে বাধাকে সংকেত দেয়।
প্রতিটি কর্মী গোরুটিন কিছু কাজ সম্পাদন করে এবং তারপর কাউন্টার ভেরিয়েবল বাড়ানোর জন্য লকটি অর্জন করে। যদি বর্তমান কর্মী ব্যারিয়ারে পৌঁছানোর শেষ একজন হয়, তবে এটি সমস্ত অপেক্ষমাণ কর্মীদের জাগানোর জন্য cond.Broadcast()
ব্যবহার করে বাধার অবস্থা সম্প্রচার করে। অন্যথায়, এটি cond.Wait()
ব্যবহার করে শেষ কর্মী দ্বারা অবহিত হওয়ার জন্য বাধার উপর অপেক্ষা করে।
বাধা সিঙ্ক্রোনাইজেশন নিশ্চিত করে যে সমস্ত কর্মী গোরুটিনগুলি বাধা অতিক্রম করার আগে তাদের মধ্যে পৌঁছে যায়। এটি তাদের কর্মপ্রবাহের একটি নির্দিষ্ট বিন্দুতে একাধিক গোরুটিন সম্পাদনের সিঙ্ক্রোনাইজেশনের প্রয়োজন এমন পরিস্থিতিতে কার্যকর হতে পারে।
মনে রাখবেন যে এই উদাহরণে একটি সাধারণ কাউন্টার ব্যবহার করে বাধা প্রয়োগ করা হয়েছে। যাইহোক, আরও জটিল পরিস্থিতিতে, সঠিক সিঙ্ক্রোনাইজেশন নিশ্চিত করতে এবং রেসের অবস্থা এড়াতে আপনাকে অতিরিক্ত সিঙ্ক্রোনাইজেশন প্রক্রিয়া বা শর্ত বিবেচনা করতে হতে পারে।
উপসংহারে, গো প্রোগ্রামিং ভাষায় sync.Cond
একটি দরকারী প্রকার যা নির্দিষ্ট অবস্থার উপর ভিত্তি করে গরউটিনগুলির মধ্যে সমন্বয় এবং সমন্বয়ের অনুমতি দেয়। এটি শর্ত ভেরিয়েবল তৈরি এবং পরিচালনা করার একটি উপায় প্রদান করে। এটির জন্য অপেক্ষা করার, সংকেত দেওয়ার এবং সম্প্রচারের অবস্থার জন্য পদ্ধতি রয়েছে৷ `sync.Cond` ব্যবহার করে, আপনি Go-এ আরও নিয়ন্ত্রিত এবং সিঙ্ক্রোনাইজ করা সমসাময়িক প্রোগ্রাম লিখতে পারেন।
এটা মনে রাখা গুরুত্বপূর্ণ যে sync.Cond
হল গো স্ট্যান্ডার্ড লাইব্রেরি দ্বারা প্রদত্ত সিঙ্ক্রোনাইজেশন আদিমগুলির মধ্যে একটি, এবং এটির ব্যবহার আপনার সমসাময়িক প্রোগ্রামের নির্দিষ্ট প্রয়োজনীয়তার উপর নির্ভর করে। কিছু ক্ষেত্রে, চ্যানেল বা sync.WaitGroup
মতো অন্যান্য সিঙ্ক্রোনাইজেশন আদিম। ওয়েটগ্রুপ আরও উপযুক্ত হতে পারে।