การจัดการทรัพยากร (คอมพิวเตอร์)

ในการเขียนโปรแกรมคอมพิวเตอร์การจัดการทรัพยากรหมายถึงเทคนิคในการจัดการทรัพยากร (ส่วนประกอบที่มีความพร้อมใช้งานจำกัด)

โปรแกรมคอมพิวเตอร์อาจจัดการทรัพยากรของตนเอง[ ซึ่ง? ]โดยใช้คุณสมบัติที่เปิดเผยโดยภาษาการเขียนโปรแกรม (Elder, Jackson & Liblit (2008) เป็นบทความสำรวจที่เปรียบเทียบแนวทางที่แตกต่างกัน) หรืออาจเลือกที่จะจัดการโดยโฮสต์ - ระบบปฏิบัติการหรือเครื่องเสมือน - หรือโปรแกรมอื่น

การจัดการตามโฮสต์เรียกว่าการติดตามทรัพยากรและประกอบด้วยการล้างข้อมูลรั่วไหลของทรัพยากร: ยุติการเข้าถึงทรัพยากรที่ได้รับมาแต่ไม่ได้ปล่อยออกมาหลังการใช้งาน สิ่งนี้เรียกว่าการเรียกคืนทรัพยากร และคล้ายคลึงกับการรวบรวมขยะสำหรับหน่วยความจำ ในหลายระบบ ระบบปฏิบัติการจะเรียกคืนรีซอร์สหลังจาก ที่ กระบวนการออกจาก การเรียกระบบ

การควบคุมการเข้าถึง

การละเว้นการปล่อยทรัพยากรเมื่อโปรแกรมใช้งานเสร็จแล้ว เรียกว่าการรั่วไหลของทรัพยากรและเป็นปัญหาในการประมวลผลตามลำดับ กระบวนการต่างๆ ที่ต้องการเข้าถึงทรัพยากรที่มีจำกัดอาจเป็นปัญหาในการประมวลผลพร้อมกันและเรียกว่าการ ช่วงชิงทรัพยากร

การจัดการทรัพยากรพยายามควบคุมการเข้าถึงเพื่อป้องกันทั้งสองสถานการณ์นี้

ทรัพยากรรั่วไหล

อย่างเป็นทางการ การจัดการทรัพยากร (การป้องกันการรั่วไหลของทรัพยากร) ประกอบด้วยการตรวจสอบให้แน่ใจว่าทรัพยากรได้รับการเผยแพร่หากได้รับมาสำเร็จเท่านั้น ปัญหาทั่วไปนี้สามารถสรุปได้เป็นโค้ด " before, bodyและafter " ซึ่งโดยปกติจะดำเนินการตามลำดับนี้ โดยมีเงื่อนไขว่าโค้ดafterจะถูกเรียกก็ต่อเมื่อ โค้ด beforeเสร็จสมบูรณ์แล้วเท่านั้น โดยไม่คำนึงว่าโค้ดBody จะเป็นอย่างไร ดำเนินการได้สำเร็จหรือไม่ สิ่งนี้เรียกอีกอย่างว่ารันรอบ[1]หรือแซนด์วิชโค้ดและเกิดขึ้นในบริบทอื่นๆ มากมาย[2]เช่น การเปลี่ยนแปลงสถานะของโปรแกรมชั่วคราว หรือการติดตามรายการและทางออกในรูทีนย่อย อย่างไรก็ตาม การจัดการทรัพยากรเป็นแอปพลิเคชันที่ถูกอ้างถึงบ่อยที่สุด ในการเขียนโปรแกรมเชิงแง่มุมการดำเนินการดังกล่าวเกี่ยวกับตรรกะเป็นรูปแบบหนึ่งของคำแนะนำ

ในศัพท์เฉพาะของการวิเคราะห์การไหลของการควบคุมการปล่อยทรัพยากรจะต้องมีผลเหนือกว่าการได้มาซึ่งทรัพยากรที่ประสบความสำเร็จ[3]ความล้มเหลวในการตรวจสอบให้แน่ใจว่านี่เป็นจุดบกพร่อง และเส้นทางโค้ดที่ละเมิดเงื่อนไขนี้ทำให้เกิดการรั่วไหลของทรัพยากร การรั่วไหลของทรัพยากรมักเป็นปัญหาเล็กน้อย โดยทั่วไปไม่ทำให้โปรแกรมหยุดทำงาน แต่กลับทำให้โปรแกรมหรือระบบโดยรวมช้าลง[2]อย่างไรก็ตาม สิ่งเหล่านี้อาจทำให้เกิดการขัดข้อง - ทั้งตัวโปรแกรมเองหรือโปรแกรมอื่น ๆ - เนื่องจากทรัพยากรหมด:หากระบบหมดทรัพยากร คำขอรับข้อมูลจะล้มเหลว สิ่งนี้อาจทำให้เกิดข้อบกพร่องด้านความปลอดภัยหากการโจมตีอาจทำให้ทรัพยากรหมด การรั่วไหลของทรัพยากรอาจเกิดขึ้นภายใต้โฟลว์โปรแกรมปกติ เช่น เพียงลืมที่จะปล่อยทรัพยากร - หรือเฉพาะในสถานการณ์พิเศษเท่านั้น เช่น เมื่อทรัพยากรไม่ได้รับการปล่อยหากมีข้อยกเว้นในส่วนอื่นของโปรแกรม การรั่วไหลของทรัพยากรมักเกิดจาก การออก จากรูทีนย่อยก่อนreturn เวลา ไม่ว่าจะโดย คำสั่ง หรือข้อยกเว้นที่เกิดจากรูทีนย่อยเอง หรือรูทีนย่อยที่ลึกกว่าที่เรียกใช้ ในขณะที่การปล่อยทรัพยากรเนื่องจากการส่งคืนคำสั่งสามารถจัดการได้โดยการปล่อยอย่างระมัดระวังภายในรูทีนย่อยก่อนที่จะส่งคืน ข้อยกเว้นไม่สามารถจัดการได้หากไม่มีสิ่งอำนวยความสะดวกทางภาษาเพิ่มเติมบางอย่างที่รับประกันว่าโค้ดการเผยแพร่จะถูกดำเนินการ

การซื้อทรัพยากรที่ประสบความสำเร็จต้องควบคุมการปล่อยทรัพยากรอย่างละเอียดยิ่งขึ้น มิฉะนั้นโค้ดจะพยายามปล่อยทรัพยากรที่ไม่ได้รับ ผลที่ตามมาของการเปิดตัวที่ไม่ถูกต้องดังกล่าวมีตั้งแต่การถูกเพิกเฉยอย่างเงียบๆ ไปจนถึงการหยุดทำงานของโปรแกรมหรือพฤติกรรมที่คาดเดาไม่ได้ โดยทั่วไปข้อบกพร่องเหล่านี้มักไม่แสดงออกมา เนื่องจากจำเป็นต้องจัดสรรทรัพยากรจึงจะล้มเหลวก่อน ซึ่งโดยทั่วไปจะเป็นกรณีพิเศษ นอกจากนี้ ผลที่ตามมาอาจไม่ร้ายแรง เนื่องจากโปรแกรมอาจหยุดทำงานอยู่แล้วเนื่องจากไม่สามารถรับทรัพยากรที่จำเป็นได้ อย่างไรก็ตาม สิ่งเหล่านี้สามารถป้องกันการกู้คืนจากความล้มเหลว หรือเปลี่ยนการปิดระบบตามระเบียบให้เป็นการปิดระบบที่ไม่เป็นระเบียบ โดยทั่วไปเงื่อนไขนี้จะได้รับการรับประกันโดยการตรวจสอบครั้งแรกว่าได้รับทรัพยากรสำเร็จก่อนที่จะปล่อย โดยมีตัวแปรบูลีนในการบันทึก "ได้มาสำเร็จ" ซึ่งขาดอะตอมมิกซิตีหากได้รับทรัพยากร แต่ตัวแปรแฟล็กไม่สามารถอัปเดตได้ หรือในทางกลับกัน – หรือโดยตัวจัดการทรัพยากรที่เป็นประเภท nullableโดยที่ "null" หมายถึง "ไม่ได้รับมาสำเร็จ" ซึ่งจะทำให้มั่นใจถึงความเป็นอะตอมมิก

การโต้แย้งทรัพยากร

การจัดการหน่วยความจำ

หน่วยความจำสามารถถือเป็นทรัพยากรได้ แต่ โดยทั่วไป การจัดการหน่วยความจำจะพิจารณาแยกต่างหาก โดยหลักแล้ว เนื่องจากการจัดสรรหน่วยความจำและการจัดสรรคืนเกิดขึ้นบ่อยกว่าการได้มาและปล่อยทรัพยากรอื่นๆ เช่น ตัวจัดการไฟล์ หน่วยความจำที่จัดการโดย ระบบ ภายนอกมีความคล้ายคลึงกับทั้งการจัดการหน่วยความจำ (ภายใน) (เนื่องจากเป็นหน่วยความจำ) และการจัดการทรัพยากร (เนื่องจากได้รับการจัดการโดยระบบภายนอก) ตัวอย่างรวมถึงหน่วยความจำที่จัดการผ่านโค้ดเนทีฟและใช้จาก Java (ผ่านJava Native Interface ); และออบเจ็กต์ในDocument Object Model ( DOM ) ที่ใช้จากJavaScriptในทั้งสองกรณีนี้ตัวจัดการหน่วยความจำ ( ตัวรวบรวมขยะ ) ของสภาพแวดล้อมรันไทม์ (เครื่องเสมือน) ไม่สามารถจัดการหน่วยความจำภายนอกได้ (ไม่มีการจัดการหน่วยความจำที่ใช้ร่วมกัน) ดังนั้นหน่วยความจำภายนอกจึงถือเป็นทรัพยากร และจัดการแบบอะนาล็อก . อย่างไรก็ตาม วงจรระหว่างระบบ (JavaScript อ้างอิงถึง DOM อ้างอิงกลับไปยัง JavaScript) อาจทำให้การจัดการยากหรือเป็นไปไม่ได้

การจัดการคำศัพท์และการจัดการที่ชัดเจน

ความแตกต่างที่สำคัญในการจัดการทรัพยากรภายในโปรแกรมคือระหว่างการจัดการคำศัพท์และการจัดการที่ชัดเจน - ไม่ว่าทรัพยากรสามารถจัดการโดยมีขอบเขตคำศัพท์หรือไม่ เช่น ตัวแปรสแต็ก (อายุการใช้งานถูกจำกัดไว้ที่ขอบเขตคำศัพท์เดียว ซึ่งได้มาเมื่อเข้าสู่หรือ ภายในขอบเขตเฉพาะ และเผยแพร่เมื่อการดำเนินการออกจากขอบเขตนั้น) หรือว่าทรัพยากรจะต้องได้รับการจัดสรรและปล่อยอย่างชัดเจน เช่น ทรัพยากรที่ได้มาภายในฟังก์ชันแล้วส่งคืนจากนั้น ซึ่งจะต้องถูกปล่อยออกมานอกฟังก์ชันการรับ การจัดการคำศัพท์ (ถ้ามี) ช่วยให้สามารถแยกแยะข้อกังวลได้ดีขึ้นและเกิดข้อผิดพลาดน้อยลง

เทคนิคพื้นฐาน

แนวทางพื้นฐานในการจัดการทรัพยากรคือการได้มาซึ่งทรัพยากร ดำเนินการบางอย่างกับทรัพยากรนั้น จากนั้นปล่อยมันออก โดยให้โค้ดในรูปแบบ (แสดงด้วยการเปิดไฟล์ใน Python):

f  =  เปิด( ชื่อไฟล์) 
... 
f . ปิด()

สิ่งนี้ถูกต้องหาก...โค้ดแทรกแซงไม่มีการออกก่อนเวลา ( return) ภาษาไม่มีข้อยกเว้น และopenรับประกันว่าจะสำเร็จ อย่างไรก็ตาม จะทำให้ทรัพยากรรั่วไหลหากมีการส่งคืนหรือข้อยกเว้น และทำให้เกิดการเผยแพร่ทรัพยากรที่ไม่ได้รับอย่างไม่ถูกต้องหากopenสามารถล้มเหลวได้

มีปัญหาพื้นฐานอีกสองประการ: คู่การได้มา-การเผยแพร่ไม่ได้อยู่ติดกัน (รหัสการเผยแพร่ต้องเขียนให้ห่างจากรหัสการได้มา) และการจัดการทรัพยากรไม่ได้ถูกห่อหุ้ม – โปรแกรมเมอร์จะต้องตรวจสอบให้แน่ใจด้วยตนเองว่าจับคู่กันอยู่เสมอ เมื่อรวมกันแล้ว สิ่งเหล่านี้หมายความว่าการได้มาและการเผยแพร่จะต้องจับคู่กันอย่างชัดเจน แต่ไม่สามารถนำมารวมกันได้ ดังนั้นจึงทำให้ง่ายต่อการจับคู่กันอย่างถูกต้อง

การรั่วไหลของทรัพยากรสามารถแก้ไขได้ในภาษาที่รองรับfinallyโครงสร้าง (เช่น Python) โดยการวางเนื้อหาไว้ในอนุtryประโยค และเผยแพร่ในอนุfinallyประโยค:

f  =  open ( ชื่อไฟล์) 
ลอง: 
    ... 
สุดท้าย: 
    f ปิด()

สิ่งนี้ทำให้แน่ใจได้ว่าการปลดปล่อยที่ถูกต้องแม้ว่าจะมีการส่งคืนภายในร่างกายหรือมีข้อยกเว้นเกิดขึ้นก็ตาม นอกจากนี้ โปรดสังเกตว่าการได้มาเกิดขึ้นก่อนส่วนtryคำสั่ง เพื่อให้มั่นใจว่าfinallyส่วนคำสั่งจะถูกดำเนินการก็ต่อเมื่อopenโค้ดสำเร็จ (โดยไม่มีข้อยกเว้น) โดยสมมติว่า "ไม่มีข้อยกเว้น" หมายถึง "ความสำเร็จ" (ดังเช่นในกรณีopenของ Python) หากการได้มาซึ่งทรัพยากรสามารถล้มเหลวโดยไม่มีข้อยกเว้น เช่น โดยการส่งคืนรูปแบบของnullจะต้องตรวจสอบก่อนที่จะเผยแพร่ เช่น:

f  =  open ( ชื่อไฟล์) 
ลอง: 
    ... 
สุดท้าย: 
    ถ้า f : 
        f ปิด()

แม้ว่าสิ่งนี้จะช่วยให้มั่นใจได้ถึงการจัดการทรัพยากรที่ถูกต้อง แต่ก็ไม่สามารถจัดเตรียมการเสริมหรือการห่อหุ้มได้ ในหลายภาษา มีกลไกที่ให้การห่อหุ้ม เช่นwithคำสั่งใน Python:

ด้วย open ( ชื่อไฟล์)  เป็น f : 
    ...

เทคนิคข้างต้น – unwind Protection ( finally) และการห่อหุ้มบางรูปแบบ – เป็นแนวทางที่ใช้กันทั่วไปในการจัดการทรัพยากร ซึ่งพบได้ในรูปแบบต่างๆ ใน ​​C#, Common Lisp , Java, Python, Ruby, SchemeและSmalltalk [ 1]ท่ามกลางคนอื่นๆ พวกเขามีอายุในช่วงปลายทศวรรษ 1970 ใน ภาษาถิ่น NILของ Lisp; ดูการจัดการข้อยกเว้น §ประวัติ การนำไปปฏิบัติมีหลากหลายรูปแบบ และยังมีแนวทางที่แตกต่างกันอย่างมีนัยสำคัญอีกด้วย

แนวทาง

ผ่อนคลายการป้องกัน

แนวทางทั่วไปในการจัดการทรัพยากรข้ามภาษาคือการใช้การป้องกันแบบคลาย ซึ่งเรียกว่าเมื่อการดำเนินการออกจากขอบเขต โดยการดำเนินการที่วิ่งออกจากจุดสิ้นสุดของบล็อก การส่งคืนจากภายในบล็อก หรือมีข้อยกเว้นเกิดขึ้น ซึ่งใช้ได้กับทรัพยากรที่มีการจัดการสแต็ก และมีการนำไปใช้ในหลายภาษา รวมถึง C#, Common Lisp, Java, Python, Ruby และ Scheme ปัญหาหลักของแนวทางนี้คือ Release Code (โดยส่วนใหญ่อยู่ในอนุfinallyประโยค) อาจอยู่ห่างจาก Acquisition Code มาก (ไม่มีadjacency ) และรหัส Acquisition และ Release จะต้องจับคู่กันโดยผู้เรียกเสมอ (ขาดการห่อหุ้ม ) ). สิ่งเหล่านี้สามารถแก้ไขได้ทั้งเชิงหน้าที่ โดยใช้การปิด/การเรียกกลับ/โครูทีน (Common Lisp, Ruby, Scheme) หรือโดยการใช้อ็อบเจ็กต์ที่จัดการทั้งการได้มาและการรีลีส และเพิ่มโครงสร้างภาษาเพื่อเรียกใช้เมธอดเหล่านี้เมื่อการควบคุมเข้าและออก ขอบเขต (C# using, Java try-with-resources, Python with); ดูด้านล่าง

อีกทางเลือกหนึ่งและมีความจำเป็นมากกว่าคือการเขียนโค้ดอะซิงโครนัสในรูปแบบโดยตรง : รับทรัพยากร จากนั้นในบรรทัดถัดไปจะมี การเผยแพร่แบบ เลื่อนออกไปซึ่งเรียกว่าเมื่อออกจากขอบเขต – การได้มาแบบซิงโครนัสตามด้วยการเปิดตัวแบบอะซิงโครนัส สิ่งนี้มีต้นกำเนิดใน C++ ในฐานะคลาส ScopeGuard โดยAndrei Alexandrescuและ Petru Marginean ในปี 2000 [4]พร้อมการปรับปรุงโดย Joshua Lehrer [5]และมีการสนับสนุนภาษาโดยตรงในภาษา D ผ่านscopeคำหลัก (ScopeGuardStatement) ซึ่งเป็นแนวทางหนึ่งในการความปลอดภัยข้อยกเว้นนอกเหนือจาก RAII (ดูด้านล่าง) [6]มันถูกรวมไว้ใน Go ด้วยเช่นdeferกัน[7]แนวทางนี้ขาดการห่อหุ้ม - เราต้องจับคู่การได้มาและการเผยแพร่อย่างชัดเจน - แต่หลีกเลี่ยงการสร้างอ็อบเจ็กต์สำหรับทรัพยากรแต่ละอย่าง (ใช้โค้ดได้ดี หลีกเลี่ยงการเขียนคลาสสำหรับทรัพยากรแต่ละประเภท)

การเขียนโปรแกรมเชิงวัตถุ

ในการเขียนโปรแกรมเชิงวัตถุทรัพยากรจะถูกห่อหุ้มภายในวัตถุที่ใช้ทรัพยากรเหล่านั้น เช่นfileอ็อบเจ็กต์ที่มีฟิลด์ซึ่งมีค่าเป็นfile descriptor (หรือตัวจัดการไฟล์ ทั่วไป ) สิ่งนี้ทำให้ออบเจ็กต์สามารถใช้และจัดการทรัพยากรโดยที่ผู้ใช้ออบเจ็กต์ไม่จำเป็นต้องทำเช่นนั้น อย่างไรก็ตาม มีหลายวิธีที่สามารถเชื่อมโยงวัตถุและทรัพยากรเข้าด้วยกันได้

ประการแรก มีคำถามเรื่องการเป็นเจ้าของ: วัตถุมีทรัพยากรหรือไม่?

  • วัตถุสามารถเป็นเจ้าของทรัพยากรได้ (ผ่านองค์ประกอบของวัตถุความสัมพันธ์ "มีความสัมพันธ์") ที่แข็งแกร่ง
  • วัตถุสามารถดูทรัพยากรได้ (ผ่านการรวมวัตถุ ความสัมพันธ์ "มีความสัมพันธ์") ที่อ่อนแอ)
  • วัตถุสามารถสื่อสารกับวัตถุอื่น ๆ ที่มีทรัพยากร (ผ่านAssociation )

อ็อบเจ็กต์ที่มีทรัพยากรสามารถรับและเผยแพร่ได้หลายวิธี ณ จุดต่างๆ ตลอดอายุของอ็อบเจ็กต์ สิ่งเหล่านี้เกิดขึ้นเป็นคู่ แต่ในทางปฏิบัติมักไม่ได้ใช้แบบสมมาตร (ดูด้านล่าง):

  • รับ/ปล่อยในขณะที่วัตถุนั้นถูกต้อง ผ่านวิธีการ (อินสแตนซ์) เช่นopenหรือdispose.
  • รับ/ปล่อยระหว่างการสร้าง/ทำลายออบเจ็กต์ (ใน Initializer และ Finalizer)
  • ไม่ได้รับหรือปล่อยทรัพยากร แทนที่จะเพียงแค่มีมุมมองหรือการอ้างอิงถึงทรัพยากรที่ได้รับการจัดการภายนอกกับอ็อบเจ็กต์ เช่นเดียวกับในการฉีดการพึ่งพาโดยชัดแจ้ง วัตถุที่มีทรัพยากร (หรือสามารถสื่อสารกับสิ่งที่ทำ) จะถูกส่งผ่านเป็นอาร์กิวเมนต์ไปยังเมธอดหรือคอนสตรัคเตอร์

ที่พบบ่อยที่สุดคือการรับทรัพยากรระหว่างการสร้างออบเจ็กต์ จากนั้นจึงเผยแพร่อย่างชัดเจนผ่านวิธีการอินสแตนซ์ ซึ่งเรียกกันทั่วไปdisposeว่า สิ่งนี้คล้ายคลึงกับการจัดการไฟล์แบบดั้งเดิม (ได้รับระหว่างopenเผยแพร่โดยชัดแจ้งclose) และเป็นที่รู้จักในชื่อรูปแบบการกำจัดนี่เป็นแนวทางพื้นฐานที่ใช้ในภาษาเชิงวัตถุสมัยใหม่หลักๆ หลายภาษา รวมถึงJava , C#และPythonและภาษาเหล่านี้มีโครงสร้างเพิ่มเติมเพื่อทำให้การจัดการทรัพยากรเป็นแบบอัตโนมัติ อย่างไรก็ตาม แม้แต่ในภาษาเหล่านี้ ความสัมพันธ์ของวัตถุที่ซับซ้อนมากขึ้นยังส่งผลให้เกิดการจัดการทรัพยากรที่ซับซ้อนมากขึ้น ตามที่กล่าวไว้ด้านล่าง

ไร่

แนวทางธรรมชาติคือการทำให้การถือครองทรัพยากรเป็นค่าคงที่ของคลาส : ทรัพยากรได้มาระหว่างการสร้างวัตถุ (การกำหนดค่าเริ่มต้นโดยเฉพาะ) และปล่อยออกมาระหว่างการทำลายวัตถุ (การสรุปโดยเฉพาะ) สิ่งนี้เรียกว่าResource Acquisition Is Initialization (RAII) และเชื่อมโยงการจัดการทรัพยากรกับอายุการใช้งานของอ็อบเจ็กต์เพื่อให้มั่นใจว่าอ็อบเจ็กต์ที่มีชีวิตมีทรัพยากรที่จำเป็นทั้งหมด วิธีการอื่นๆ ไม่ได้ทำให้การถือครองทรัพยากรเป็นคลาสคงที่ และดังนั้นวัตถุจึงอาจไม่มีทรัพยากรที่จำเป็น (เนื่องจากยังไม่ได้รับมา ได้รับการเผยแพร่แล้ว หรือได้รับการจัดการจากภายนอก) ส่งผลให้เกิดข้อผิดพลาด เช่น การพยายามอ่าน จากไฟล์ที่ถูกปิด แนวทางนี้เชื่อมโยงการจัดการทรัพยากรกับการจัดการหน่วยความจำ (โดยเฉพาะการจัดการอ็อบเจ็กต์) ดังนั้นหากไม่มีหน่วยความจำรั่ว (ไม่มีอ็อบเจ็กต์รั่วไหล) ก็จะไม่มีทรัพยากรรั่วไหล RAII ทำงานตามธรรมชาติสำหรับทรัพยากรที่มีการจัดการแบบฮีป ไม่เพียงแต่ทรัพยากรที่จัดการแบบสแต็กเท่านั้น และสามารถประกอบได้: ทรัพยากรที่ถือครองโดยอ็อบเจ็กต์ในความสัมพันธ์ที่ซับซ้อนตามอำเภอใจ ( กราฟวัตถุ ที่ซับซ้อน ) จะถูกปล่อยออกมาอย่างโปร่งใสโดยการทำลายวัตถุ (ตราบใดที่ทำอย่างถูกต้อง! ).

RAII เป็นแนวทางการจัดการทรัพยากรมาตรฐานใน C++ แต่ไม่ค่อยมีใครใช้ภายนอก C++ แม้ว่าจะดูน่าสนใจก็ตาม เนื่องจากทำงานได้ไม่ดีกับการจัดการหน่วยความจำอัตโนมัติสมัยใหม่ โดยเฉพาะการติดตามการรวบรวมขยะ : RAII เชื่อมโยงการจัดการทรัพยากรกับการจัดการหน่วยความจำ แต่สิ่งเหล่านี้มีความแตกต่างอย่างมีนัยสำคัญ . ประการแรก เนื่องจากทรัพยากรมีราคาแพง จึงควรปล่อยทิ้งทันที ดังนั้นวัตถุที่กักเก็บทรัพยากรควรถูกทำลายทันทีที่กลายเป็นขยะ (ไม่ได้ใช้งานอีกต่อไป) การทำลายอ็อบเจ็กต์เกิดขึ้นทันทีในการจัดการหน่วยความจำที่กำหนด เช่น ใน C++ (อ็อบเจ็กต์ที่จัดสรรสแต็กจะถูกทำลายในสแต็กคลี่คลาย อ็อบเจ็กต์ที่จัดสรรฮีปจะถูกทำลายด้วยตนเองผ่านการเรียกdeleteหรือใช้โดยอัตโนมัติunique_ptr) หรือในการนับอ้างอิงตามที่กำหนด (โดยที่อ็อบเจ็กต์จะถูกทำลายทันทีเมื่อ จำนวนการอ้างอิงลดลงเหลือ 0) ดังนั้น RAII จึงทำงานได้ดีในสถานการณ์เหล่านี้ อย่างไรก็ตาม การจัดการหน่วยความจำอัตโนมัติที่ทันสมัยที่สุดนั้นไม่สามารถกำหนดได้ จึงไม่รับประกันว่าวัตถุจะถูกทำลายทันทีหรือแม้กระทั่งเลย! เนื่องจากว่าการทิ้งขยะโดยจัดสรรไว้นั้นถูกกว่าการเก็บแต่ละวัตถุอย่างแม่นยำทันทีที่มันกลายเป็นขยะ ประการที่สอง การปล่อยทรัพยากรในระหว่างการทำลายอ็อบเจ็กต์หมายความว่าอ็อบเจ็กต์ต้องมีตัวสรุป (ในการจัดการหน่วยความจำที่กำหนดขึ้นซึ่งเรียกว่าตัวทำลาย ) ซึ่งวัตถุไม่สามารถถูกจัดสรรคืนได้ง่ายๆ ซึ่งจะทำให้การรวบรวมขยะซับซ้อนและทำให้การรวบรวมขยะช้าลงอย่างมาก

ความสัมพันธ์ที่ซับซ้อน

เมื่อออบเจ็กต์หลายรายการพึ่งพาทรัพยากรเดียว การจัดการทรัพยากรอาจมีความซับซ้อน

คำถามพื้นฐานคือว่าความสัมพันธ์ "มีความสัมพันธ์" เป็นการเป็นเจ้าของวัตถุอื่น ( องค์ประกอบของวัตถุ ) หรือการดูวัตถุอื่น ( การรวมวัตถุ ) กรณีทั่วไปคือเมื่อวัตถุสองชิ้นถูกโยงเข้าด้วยกัน เช่น ในรูปแบบไปป์และตัวกรอง รูป แบบการมอบหมายรูปแบบการตกแต่งหรือรูปแบบอะแด็ปเตอร์หากออบเจ็กต์ที่สอง (ซึ่งไม่ได้ใช้โดยตรง) ถือทรัพยากรไว้ ออบเจ็กต์แรก (ซึ่งใช้โดยตรง) รับผิดชอบในการจัดการทรัพยากรหรือไม่ โดยทั่วไปแล้วคำตอบนี้จะเหมือนกันกับว่าออบเจ็กต์แรกเป็นเจ้าของออบเจ็กต์ที่สองหรือไม่ หากเป็นเช่นนั้น วัตถุที่เป็นเจ้าของจะต้องรับผิดชอบในการจัดการทรัพยากรด้วย ("การมีทรัพยากร" เป็นแบบสกรรมกริยา ) ในขณะที่ถ้าไม่ แสดงว่าไม่มี นอกจากนี้ วัตถุชิ้นเดียวอาจ "มี" วัตถุอื่น ๆ อีกหลายชิ้น โดยเป็นเจ้าของบางส่วนและดูวัตถุอื่น ๆ

โดยทั่วไปจะพบทั้งสองกรณี และมีแบบแผนที่แตกต่างกัน การมีอ็อบเจ็กต์ที่ใช้ทรัพยากรโดยอ้อมต้องรับผิดชอบทรัพยากร (องค์ประกอบ) ทำให้เกิดการห่อหุ้ม (สิ่งหนึ่งต้องการเพียงอ็อบเจ็กต์ที่ไคลเอนต์ใช้ โดยไม่มีอ็อบเจ็กต์แยกสำหรับทรัพยากร) แต่ส่งผลให้เกิดความซับซ้อนอย่างมาก โดยเฉพาะอย่างยิ่งเมื่อทรัพยากรถูกใช้ร่วมกันโดยหลายอ็อบเจ็กต์หรือ วัตถุมีความสัมพันธ์ที่ซับซ้อน ถ้าเฉพาะวัตถุที่ใช้ทรัพยากรโดยตรงเท่านั้นที่รับผิดชอบทรัพยากร (การรวม) ความสัมพันธ์ระหว่างวัตถุอื่น ๆ ที่ใช้ทรัพยากรสามารถละเว้นได้ แต่ไม่มีการห่อหุ้ม (นอกเหนือจากวัตถุที่ใช้โดยตรง): ทรัพยากรจะต้องได้รับการจัดการโดยตรง และอาจไม่สามารถใช้ได้กับวัตถุที่ใช้งานทางอ้อม (หากได้รับการเผยแพร่แยกต่างหาก)

ในการใช้งานอย่างชาญฉลาด ในองค์ประกอบของวัตถุ หากใช้รูปแบบการกำจัด วัตถุที่เป็นเจ้าของก็จะมีdisposeวิธีการเช่นกัน ซึ่งจะเรียกdisposeวิธีการของวัตถุที่เป็นเจ้าของซึ่งจะต้องถูกกำจัด ใน RAII สิ่งนี้จะถูกจัดการโดยอัตโนมัติ (ตราบใดที่วัตถุที่เป็นเจ้าของจะถูกทำลายโดยอัตโนมัติ: ใน C ++ หากเป็นค่าหรือ a unique_ptrแต่ไม่ใช่ตัวชี้ดิบ: ดูความเป็นเจ้าของตัวชี้) ในการรวมอ็อบเจ็กต์ ออบเจ็กต์การดูไม่จำเป็นต้องทำอะไร เนื่องจากออบเจ็กต์จะไม่รับผิดชอบต่อทรัพยากร

ทั้งสองพบได้ทั่วไป ตัวอย่างเช่น ในJava Class LibraryปิดReader#close()สตรีมที่สำคัญ และสามารถเชื่อมโยงกันได้ ตัวอย่างเช่น a BufferedReaderอาจมี a InputStreamReaderซึ่งในทางกลับกันจะมี a FileInputStreamและการเรียกcloseในBufferedReaderทางกลับกันจะปิดInputStreamReaderซึ่งในทางกลับกันจะปิดFileInputStreamซึ่งในทางกลับกันจะปล่อยทรัพยากรไฟล์ระบบ แท้จริงแล้ว ออบเจ็กต์ที่ใช้ทรัพยากรโดยตรงอาจไม่เปิดเผยตัวตนได้ ต้องขอบคุณการห่อหุ้ม:

ลอง( BufferedReader reader = new BufferedReader ( new InputStreamReader ( new FileInputStream ( fileName )))) { // ใช้ reader. } // เครื่องอ่านจะถูกปิดเมื่อออกจากบล็อก try-with-resources ซึ่งจะปิดแต่ละออบเจ็กต์ที่มีอยู่ตามลำดับ        
    


อย่างไรก็ตาม คุณยังสามารถจัดการเฉพาะออบเจ็กต์ที่ใช้ทรัพยากรโดยตรง และไม่ใช้การจัดการทรัพยากรบนออบเจ็กต์ Wrapper:

ลอง( FileInputStream stream = new FileInputStream ( fileName )))) { BufferedReader reader = new BufferedReader ( InputStreamReader ใหม่( stream )); // ใช้เครื่องอ่าน} // สตรีมถูกปิดเมื่อออกจากบล็อก try-with-resources // ตัวอ่านไม่สามารถใช้งานได้อีกต่อไปหลังจากปิดสตรีมแล้ว แต่ตราบใดที่มันไม่หนีจากบล็อก ก็ไม่ใช่ปัญหา      
         
    



ในทางตรงกันข้าม ใน Python csv.reader ไม่ได้เป็นเจ้าของfileสิ่งที่กำลังอ่านอยู่ ดังนั้นจึงไม่จำเป็นต้อง (และเป็นไปไม่ได้) ที่จะปิดเครื่องอ่าน แต่จะfileต้องปิดตัวอ่านเอง แทน [8]

โดย มี open ( ชื่อไฟล์)  เป็น f : 
    r  =  csv เครื่องอ่าน( f ) # ใช้ r # f จะถูกปิดเมื่อออกจากคำสั่ง with และไม่สามารถใช้งานได้อีกต่อไป# ไม่ได้ทำอะไรกับ r แต่ค่า f ที่ซ่อนอยู่นั้นถูกปิด ดังนั้น r ก็ใช้ไม่ได้เช่นกัน
    


ใน.NETหลักการคือการให้ผู้ใช้ทรัพยากรโดยตรงเท่านั้นที่รับผิดชอบ: "คุณควรใช้ IDisposable ก็ต่อเมื่อประเภทของคุณใช้ทรัพยากรที่ไม่มีการจัดการโดยตรง" [9]

ในกรณีของกราฟวัตถุ ที่ซับซ้อนมากขึ้น เช่น วัตถุหลายชิ้นใช้ทรัพยากรร่วมกัน หรือวงจรระหว่างวัตถุที่เก็บทรัพยากร การจัดการทรัพยากรที่เหมาะสมอาจค่อนข้างซับซ้อน และปัญหาเดียวกันนี้เกิดขึ้นอย่างแน่นอนในการสรุปวัตถุ (ผ่านตัวทำลายหรือตัวสรุป) ตัวอย่างเช่นปัญหาผู้ฟังที่ผ่านไปสามารถเกิดขึ้นได้และทำให้ทรัพยากรรั่วไหลหากใช้ รูปแบบผู้สังเกตการณ์ (และผู้สังเกตการณ์ถือทรัพยากรไว้) มีกลไกต่างๆ มากมายที่ช่วยให้สามารถควบคุมการจัดการทรัพยากรได้ดียิ่งขึ้น ตัวอย่างเช่น ในGoogle Closed Libraryคลาสgoog.DisposableจัดเตรียมregisterDisposableวิธีการลงทะเบียนออบเจ็กต์อื่น ๆ ที่จะกำจัดด้วยออบเจ็กต์นี้ ร่วมกับอินสแตนซ์ระดับล่างและวิธีการคลาสต่าง ๆ เพื่อจัดการการกำจัด

การเขียนโปรแกรมแบบมีโครงสร้าง

ในการเขียนโปรแกรมแบบมีโครงสร้างการจัดการทรัพยากรสแต็กทำได้โดยการซ้อนโค้ดอย่างเพียงพอเพื่อรองรับทุกกรณี สิ่งนี้ต้องการเพียงการส่งคืนเพียงครั้งเดียวที่ส่วนท้ายของโค้ด และอาจส่งผลให้เกิดโค้ดที่ซ้อนกันอย่างมากหากต้องได้รับทรัพยากรจำนวนมาก ซึ่งบางคนถือว่าเป็นรูปแบบการต่อต้าน - รูปแบบการต่อต้านลูกศร[10]เนื่องจากรูปทรงสามเหลี่ยม จากการทำรังต่อเนื่องกัน

ข้อทำความสะอาด

อีกวิธีหนึ่งซึ่งอนุญาตให้ส่งคืนก่อนเวลาแต่รวมการล้างข้อมูลไว้ในที่เดียวคือการให้ฟังก์ชันส่งคืนทางออกเดียว นำหน้าด้วยโค้ดการล้างข้อมูล และใช้gotoเพื่อข้ามไปที่การล้างข้อมูลก่อนออก สิ่งนี้พบไม่บ่อยนักในโค้ดสมัยใหม่ แต่เกิดขึ้นในการใช้งานบางอย่างของ C

ดูสิ่งนี้ด้วย

อ้างอิง

  1. ↑ ab เบ็ค 1997, หน้า 37–39.
  2. ↑ ab เอ็ล เดอร์, Jackson & Liblit 2008, p. 3.
  3. เอ็ลเดอร์, แจ็กสัน & ลิบลิท 2008, หน้า. 2.
  4. "ทั่วไป: เปลี่ยนวิธีการเขียนโค้ดที่ปลอดภัยยกเว้น — ตลอดไป" โดยAndrei Alexandrescuและ Petru Marginean, 1 ธันวาคม 2000, Dr. Dobb's
  5. ScopeGuard 2.0, โจชัว เลเรอร์
  6. ^ D: ข้อยกเว้นด้านความปลอดภัย
  7. Defer, Panic, and Recover, Andrew Gerrand, The Go Blog, 4 สิงหาคม 2010
  8. Python: ไม่มี csv.close()?
  9. ^ "อินเทอร์เฟซแบบใช้แล้วทิ้งได้" ดึงข้อมูลเมื่อ2016-04-03 .
  10. Flattening Arrow Code, Jeff Atwood, 10 มกราคม 2549

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

  • การอัปเดต DG: การกำจัด การสรุปผล และการจัดการทรัพยากรโจ ดัฟฟี่

ลิงค์ภายนอก

  • การจัดการทรัพยากรเชิงกำหนด, WikiWikiWeb
แปลจาก "https://en.wikipedia.org/w/index.php?title=Resource_management_(computing)&oldid=1214848916"