Sendable — คืออะไร โปรโตคอลความปลอดภัยของเธรด

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-06-18 เวลาอ่าน: 10 นาที

Sendable คือโปรโตคอล Swift ที่ทำเครื่องหมายประเภทที่ปลอดภัยสำหรับการส่งระหว่างเธรด เมื่อทำงานกับการทำงานพร้อมกัน (actor, async/await, Task) คอมไพเลอร์ Swift กำหนดให้ข้อมูลทั้งหมดที่ส่งระหว่างบริบทที่แยก isolated ต้องสอดคล้องกับ Sendable ซึ่งป้องกันการส่งประเภทที่ไม่ปลอดภัยโดยไม่ได้ตั้งใจที่อาจนำไปสู่สภาวะการแข่งขันของข้อมูล โปรโตคอลทำหน้าที่เป็นสัญญา: ประเภทที่สอดคล้องกับ Sendable รับประกันว่าไม่มีสถานะภายในที่ไม่ถูกซิงโครไนซ์ ตาม WWDC 2021 โปรโตคอล Sendable เป็นองค์ประกอบบังคับเมื่อออกแบบสถาปัตยกรรมแบบหลายเธรดที่ปลอดภัย

ประเด็นสำคัญ

  • Sendable — โปรโตคอลที่รับประกันว่าไม่มีสภาวะการแข่งขันของข้อมูลเมื่อส่งระหว่างเธรด
  • ประเภทค่า (struct, enum) จะสอดคล้องกับ Sendable โดยอัตโนมัติหากคุณสมบัติทั้งหมดของพวกมันเป็น Sendable
  • @unchecked Sendable — กลไกบายพาสสำหรับคลาสที่นักพัฒนารับผิดชอบด้านความปลอดภัย
  • คลอเชอร์ Sendable ถูกทำเครื่องหมายด้วยแอตทริบิวต์ @Sendable ซึ่งตรวจสอบการจับตัวแปร
  • คอมไพเลอร์ แสดงข้อผิดพลาดหากประเภทที่ไม่ใช่ Sendable ถูกส่งระหว่างบริบทที่แยก isolated

โปรโตคอล Sendable ใน Swift คืออะไร?

Sendable คือโปรโตคอลจากไลบรารีมาตรฐาน Swift (SE-0302) ที่ทำเครื่องหมายประเภทที่ปลอดภัยสำหรับการส่งระหว่างโดเมนที่แยก isolated โดเมนที่แยก isolated หมายถึง actor, งาน Task หรือบริบท @MainActor Sendable ไม่มีข้อกำหนดใดๆ — มันทำหน้าที่เป็นมาร์กเกอร์ด้านความปลอดภัย

ทำไมต้องใช้ Sendable

ก่อน Swift 5.5 นักพัฒนาสามารถส่งวัตถุใดๆ ระหว่างเธรดผ่าน DispatchQueue และคอมไพเลอร์ไม่ได้ตรวจสอบว่าปลอดภัยหรือไม่ Sendable ปิดช่องโหว่นี้: ตอนนี้คอมไพเลอร์จะติดตามการส่งข้ามเธรดด้วยตัวเองและบล็อกการส่งที่ไม่ปลอดภัย ซึ่งทำให้โค้ดที่ทำงานพร้อมกันมีความน่าเชื่อถือมากขึ้นแม้ก่อนที่จะทำงาน

swift
struct UserProfile: Sendable {
    let name: String
    let age: Int
}

class NonSendableClass {
    var counter: Int = 0
}

ในตัวอย่าง UserProfile สามารถส่งระหว่างเธรดได้อย่างปลอดภัยเพราะเป็น struct ที่มีคุณสมบัติคงที่ของประเภท Sendable NonSendableClass จะทำให้เกิดข้อผิดพลาดในการคอมไพล์เมื่อพยายามส่งผ่าน await หรือเข้าไปใน Task

ประเภทใดที่สอดคล้องกับ Sendable โดยค่าเริ่มต้น

ประเภท Sendable ใน Swift แบ่งออกเป็นสามหมวดหมู่: ประเภทค่าที่มีคุณสมบัติ Sendable, คลาส final ที่มีสถานะไม่เปลี่ยนแปลง และฟังก์ชัน/คลอเชอร์ที่ทำเครื่องหมายด้วย @Sendable คอมไพเลอร์จะอนุมานความสอดคล้องกับ Sendable สำหรับประเภทค่าโดยอัตโนมัติ

ประเภทค่าและความสอดคล้องอัตโนมัติ

Struct, Enum และ tuple จะกลายเป็น Sendable โดยอัตโนมัติหากคุณสมบัติทั้งหมดและค่าที่เกี่ยวข้องของพวกมันเป็น Sendable เช่นกัน นี่เป็นแนวทางที่อนุรักษ์นิยม: หากอย่างน้อยหนึ่งคุณสมบัติไม่สอดคล้องกับ Sendable ประเภททั้งหมดจะไม่ได้รับการยอมรับว่าปลอดภัย

ประเภทSendableเงื่อนไข
Structใช่ (ไม่ต้องประกาศอย่างชัดแจ้ง)คุณสมบัติทั้งหมดเป็น Sendable
Enumใช่ (ไม่ต้องประกาศอย่างชัดแจ้ง)ค่าที่เกี่ยวข้องทั้งหมดเป็น Sendable
Final classใช่ (ต้องประกาศอย่างชัดแจ้ง)คุณสมบัติ let ทั้งหมดเป็น Sendable ไม่มี var
Non-final classไม่ไม่สามารถเป็น Sendable เนื่องจากการสืบทอด

ประเภทพื้นฐาน

ประเภทในตัวทั้งหมดของ Swift — Int, String, Double, Bool, Optional, Array, Dictionary, Set — สอดคล้องกับ Sendable ซึ่งทำให้การประกอบประเภทปลอดภัยโดยค่าเริ่มต้น นักพัฒนาเพียงต้องระวังคลาสที่กำหนดเอง

Sendable และคลาส: @unchecked Sendable

@unchecked Sendable เป็นกลไกที่อนุญาตให้คลาสประกาศตัวเองอย่างชัดแจ้งว่าเป็น Sendable โดยข้ามการตรวจสอบของคอมไพเลอร์ นักพัฒนารับผิดชอบต่อความปลอดภัยของเธรดของคลาสดังกล่าว ซึ่งมีประโยชน์สำหรับบริดจ์ Objective-C และโครงสร้างที่ปรับให้เหมาะสม

เมื่อใดควรใช้ @unchecked Sendable

@unchecked Sendable ถูกใช้เมื่อคลาสรับประกันความปลอดภัยภายในผ่านล็อกหรือการดำเนินการอะตอมมิก แต่คอมไพเลอร์ไม่สามารถตรวจสอบได้ในเชิงสถิต ตัวอย่างเช่น คลาสที่มี os_unfair_lock หรือ pthread_mutex_t — ความปลอดภัยของเธรดของมันถูกรับประกันโดยโค้ด แต่ Swift ไม่เห็นสิ่งนี้

swift
final class AtomicCounter: @unchecked Sendable {
    private var value: Int = 0
    private let lock = NSLock()
    
    func increment() {
        lock.lock()
        value += 1
        lock.unlock()
    }
}

การใช้ @unchecked Sendable ควรทำ ด้วยความระมัดระวัง มันเป็นสัญญาณที่ชัดเจนถึงนักพัฒนาคนอื่น: «ฉันตรวจสอบแล้ว มันปลอดภัย» ข้อผิดพลาดในการนำไปใช้อาจนำไปสู่สภาวะการแข่งขันที่ยากต่อการค้นหา ก่อนใช้ @unchecked ให้แน่ใจว่าประเภทนั้นไม่สามารถเขียนใหม่เป็นประเภทค่าได้จริงๆ

Sendable ร่วมกับ Actor

Actor และ Sendable เป็นเหรียญสองด้านเดียวกัน Actor แยกสถานะของมัน แต่เพื่อแลกเปลี่ยนข้อมูลกับโลกภายนอก มันต้องส่งคืนประเภท Sendable หากเมธอดของ actor ส่งคืนประเภทที่ไม่ใช่ Sendable คอมไพเลอร์จะแสดงคำเตือนหรือข้อผิดพลาด

การส่งข้อมูลผ่าน Sendable

เมื่อ actor ส่งข้อมูลไปยังโค้ดภายนอก ข้อมูลนี้ข้ามขอบเขตการแยก isolated Sendable รับประกันว่าผู้รับสามารถใช้ข้อมูลนี้อย่างปลอดภัยภายนอก actor ตัว actor เองยังคงถูกแยก isolated — สถานะภายในของมันไม่ถูกเปิดเผย

swift
struct AccountSnapshot: Sendable {
    let id: UUID
    let balance: Double
    let lastUpdated: Date
}

actor BankActor {
    private var balance: Double = 0
    
    func snapshot() async -> AccountSnapshot {
        return AccountSnapshot(
            id: UUID(),
            balance: balance,
            lastUpdated: Date()
        )
    }
}

AccountSnapshot คือ struct แบบ Sendable ที่มีเฉพาะคุณสมบัติ let ของประเภท Sendable วิธีการนี้เป็นแนวทางปฏิบัติที่ดีที่สุดสำหรับการดึงข้อมูลจาก actor ภาพรวมสถานะถูกส่งโดยค่า และ actor ไม่สูญเสียการควบคุมสถานะของมัน

ฟังก์ชันและคลอเชอร์ Sendable

@Sendable คือแอตทริบิวต์สำหรับฟังก์ชันและคลอเชอร์ที่รับประกันว่าคลอเชอร์จะไม่จับข้อมูลที่ไม่ใช่ Sendable ในรูปแบบที่เปลี่ยนแปลงได้ เมื่อคลอเชอร์ถูกส่งไปยัง Task หรือเมธอดของ actor มันจะต้องเป็น Sendable

การตรวจสอบการจับในคลอเชอร์ @Sendable

คอมไพเลอร์ตรวจสอบว่าคลอเชอร์ @Sendable ไม่จับ การอ้างอิงที่เปลี่ยนแปลงได้ ไปยังคลาส การจับคุณสมบัติ let ของประเภท Sendable อนุญาตให้ทำได้ การจับตัวแปร var ของประเภทอ้างอิงจะทำให้เกิดข้อผิดพลาดเนื่องจากคลอเชอร์อาจถูกดำเนินการพร้อมกันกับการเปลี่ยนแปลง

swift
func performAsync(operation: @Sendable () -> Void) {
    Task {
        await operation()
    }
}

let constant = "Safe"
var counter = 0
// counter — การจับ var จะเปลี่ยน counter ทำให้เกิดข้อผิดพลาดในการคอมไพล์
performAsync { // ❌ การเปลี่ยนแปลงของตัวแปรที่ถูกจับ
    print(constant)
}

กฎง่ายๆ: คลอเชอร์ @Sendable สามารถจับเฉพาะข้อมูลที่ตัวมันเองเป็น Sendable และจะไม่ถูกเปลี่ยนแปลงจากภายนอก สำหรับคลาส อนุญาตให้จับการอ้างอิงแบบอ่อน weak self หากคลาสไม่ได้ถูกทำเครื่องหมายเป็น Sendable ซึ่งป้องกันวงจรการอ้างอิงแบบคลาสสิกและสภาวะการแข่งขัน

คำถามที่พบบ่อย

Sendable ใน Swift คืออะไรในคำพูดง่ายๆ

Sendable คือมาร์กเกอร์ที่บอกคอมไพเลอร์: «ประเภทนี้สามารถส่งระหว่างเธรดได้อย่างปลอดภัย» โดยปกติ struct และค่าคงที่จะเป็น Sendable โดยค่าเริ่มต้น คลาสจะไม่เป็นเว้นแต่จะระบุอย่างชัดแจ้ง

ประเภทใดที่สอดคล้องกับ Sendable โดยอัตโนมัติ

ประเภทค่า (struct, enum) ที่มีคุณสมบัติ Sendable, คลาส final ที่มีสถานะไม่เปลี่ยนแปลง และประเภทพื้นฐาน Swift ทั้งหมด: Int, String, Double, Bool, Array, Dictionary, Optional — สอดคล้องกับ Sendable โดยอัตโนมัติ

@unchecked Sendable คืออะไร

@unchecked Sendable เป็นวิธีประกาศคลาสเป็น Sendable โดยไม่ต้องตรวจสอบเชิงสถิตจากคอมไพเลอร์ นักพัฒนารับประกันความปลอดภัยด้วยตัวเอง โดยมักผ่านล็อกหรือการดำเนินการอะตอมมิก

ทำไมต้องใช้ Sendable สำหรับ Actor

Actor แยกสถานะของมัน แต่เมื่อส่งคืนข้อมูลไปยังโค้ดภายนอก ข้อมูลนี้ข้ามขอบเขตการแยก isolated Sendable รับประกันว่าผู้รับจะไม่พบสภาวะการแข่งขันเมื่อใช้ข้อมูลนี้ภายนอก actor

วิธีทำให้คลาสเป็น Sendable ใน Swift

เพิ่มโปรโตคอล Sendable ให้กับคลาส final ที่คุณสมบัติทั้งหมดเป็นค่าคงที่ของประเภท Sendable หากคลาสใช้ล็อก สามารถใช้ @unchecked Sendable ได้ แต่ต้องใช้ความระมัดระวัง

สรุป

  • Sendable — โปรโตคอลมาร์กเกอร์สำหรับการส่งข้อมูลข้ามเธรดอย่างปลอดภัย
  • ประเภทค่า จะสอดคล้องกับ Sendable โดยอัตโนมัติเมื่อตรงตามเงื่อนไข
  • คลาส ต้องการการประกาศ Sendable หรือ @unchecked Sendable อย่างชัดแจ้ง
  • Actor ส่งคืนข้อมูลไปยังโค้ดภายนอกผ่านประเภท Sendable เท่านั้น
  • @Sendable แอตทริบิวต์คลอเชอร์ตรวจสอบความถูกต้องของการจับตัวแปร
  • คอมไพเลอร์ ตรวจสอบความสอดคล้องกับ Sendable ในเชิงสถิต ป้องกันสภาวะการแข่งขันในขณะสร้าง

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม