Sobes.tech
Middle+

რა იცით მკითხველისა და მწერლის პრობლემაზე?

sobes.tech AI

პასუხი AI-სგან

კითხვისა და მწერალის პრობლემა არის კლასიკური სინქრონიზაციის საკითხი საერთო მონაცემებზე მრავალთროდიან გარემოში.

პრობლემის არსი:

  • მრავალი ნაკადი (კითხულები) ერთდროულად კითხულობს მონაცემებს.
  • მრავალი ნაკადი (მწერალი) ერთდროულად იცვლის მონაცემებს.

მოთხოვნები:

  • კითხულები შეუძლიათ ერთდროულად კითხვა შეზღუდვების გარეშე.
  • მწერალებს აქვთ ექსკლუზიური წვდომა მონაცემებზე (სანამ მწერალი მუშაობს, სხვა არც კითხულები და არც მწერალი ვერ მიიღებს წვდომას).
  • ერთდროულად მუშაობს მხოლოდ ერთი მწერალი.
  • თუ მწერალი ელოდება წვდომას, ახალი კითხულები არ უნდა მიიღონ მასამდე, სანამ მწერალი არ დასრულებს თავის სამუშაოს. ეს წესია, რომელიც თავიდან აცილებს "მწერლების შიმშილს".

iOS-ის გადაწყვეტილებები:

  • NSLock: მარტივი მექანიზმი, მაგრამ არ არის ოპტიმალური ამ დავალებისთვის, რადგან ბლოკავს როგორც კითხვა, ასევე დაწერას.

  • NSRecursiveLock: საშუალებას აძლევს ერთ და იმავე ნაკადს მიიღოს ბლოკირება რამდენჯერმე. არ გამოიყენება.

  • NSCondition: უფრო მოქნილი მექანიზმი, რომელიც საშუალებას აძლევს ნაკადებს ელოდონ გარკვეულ პირობებს. შეიძლება გამოყენებულ იქნას კითხვა/მწერალი ლოგიკის განსახორციელებლად, მაგრამ მოითხოვს ბლოკირების და პირობების ხელით მართვას.

  • Serial Dispatch Queue (GCD): შექმენით ერთგვარი რიგი ყველა კითხვა და დაწერის ოპერაციისთვის. დაწერები შესრულდება სინქრონულად, კითხვა შეიძლება შესრულდეს ასინქრონულად, მაგრამ მხოლოდ წინამორბედ ოპერაციების დასრულების შემდეგ. ეს მარტივი გადაწყვეტილებაა, მაგრამ არ არის ოპტიმალური კითხვებისთვის, რადგან კითხვა ვერ ხორციელდება პარალელურად.

    let readWriteQueue = DispatchQueue(label: "com.example.readwrite", attributes: .concurrent)
    
    func readData() {
        readWriteQueue.async {
            // მონაცემების კითხვას ლოგიკა
            print("მონაცემების კითხვა...")
        }
    }
    
    func writeData() {
        readWriteQueue.sync(flags: .barrier) {
            // მონაცემების დაწერის ლოგიკა
            print("მონაცემების დაწერა...")
        }
    }
    
  • Concurrent Dispatch Queue with Barrier (GCD): ყველაზე ოპტიმალური გადაწყვეტილებაა უმეტეს შემთხვევებში. გამოიყენება კონკურენტული რიგი პარალელური კითხვებისთვის. დაწერებისთვის გამოიყენება .barrier ფლაგი. როდესაც ოპერაცია .barrier ფლაგით რიგში დებს, ის ელოდება ყველა წინამორბედ ოპერაციას, შემდეგ თავად შესრულდება, და სანამ იგი მიმდინარეობს, სხვა ოპერაციები (კითხვა, დაწერა) არ შესრულდება. ბარიერული ოპერაციის დასრულების შემდეგ, რიგი ისევ ხდება კონკურენტული. ასე, კითხვა პარალელურად ხორციელდება, ხოლო დაწერა ექსკლუზიურია.

    let readWriteQueue = DispatchQueue(label: "com.example.readwrite", attributes: .concurrent)
    
    func readData() {
        readWriteQueue.async {
            // მონაცემების კითხვას ლოგიკა
            print("მონაცემების კითხვა...")
        }
    }
    
    func writeData() {
        readWriteQueue.sync(flags: .barrier) {
            // მონაცემების დაწერის ლოგიკა
            print("მონაცემების დაწერა...")
        }
    }
    

    ეს იგივე კოდი არის serial queue-ისთვის, მაგრამ .concurrent ატრიბუტი ქმნის კონკურენტულ რიგს. .barrier ფლაგი აძლევს რიგს ფუნქციონირება როგორც სერიული მხოლოდ ამ კონკრეტული ოპერაციისთვის.

  • os_unfair_lock (iOS 10+): დაბალი დონე ბლოკირება, უფრო ეფექტური ვიდრე NSLock. გამოიყენება უფრო რთული კითხვა-მიწერის ლოგიკის ხელით განსახორციელებლად, თუ საჭიროა მაქსიმალური შესრულება ან სპეციფიკური ქცევა. მოითხოვს სიფრთხილეს გამოყენებაში.

შერჩევა დამოკიდებულია კონკრეტულ მოთხოვნებზე, კითხვა და დაწერის ოპერაციების სიხშირეზე, და საჭირო შესრულების დონეზე. უმეტეს შემთხვევაში, კონკურენტული დისპეჩის რიგის გამოყენება .barrier ფლაგით არის ყველაზე დაბალანსებული და რეკომენდებული მიდგომა iOS-ში.