Giới thiệu: mẫu đơn trong ứng dụng đa công nghệ đa chiều

Mô hình đơnton là một trong những mẫu thiết kế sáng tạo được sử dụng rộng rãi nhất trong kỹ thuật phần mềm. Nó đảm bảo rằng một lớp học chỉ có một thể hiện và cung cấp một điểm toàn cầu để tiếp cận ví dụ đó. Trong ứng dụng đơn đọc, thực hiện một đơnton là đơn giản: xây dựng, cung cấp một phương pháp tĩnh mà trở lại một thể hiện chủ động hoặc lazily. Tuy nhiên, trong các ứng dụng kỹ thuật đa đọc được ví dụ hệ thống, hệ thống giao dịch cao, quản lý thời gian thực, và phân phối cơ sở dữ liệu trở nên phức tạp hơn. an toàn đọc, và hiệu quả thiết kế cẩn thận trong các trường hợp nhiều lỗi, có thể dẫn đến nhiều lỗi, hoặc gỡ lỗi kỹ thuật, hoặc giảm thiểu lỗi.

Bài này xem xét những lỗi phổ biến nhất mà các nhà phát triển làm khi thực hiện mô hình Singleton trong môi trường đa đọc, giải thích những nguyên nhân tiềm ẩn, và cung cấp một tập hợp toàn diện các thực hành và mẫu tốt nhất để tránh chúng. Nó cũng bao gồm các ví dụ thực tế bằng mã Java, với các mẫu tương tự trong C++ và C# và khuyến khích các nguồn tài nguyên bên ngoài để đọc thêm.

Những lỗi thường gặp trong việc vui mừng một mình

Ngay cả những nhà phát triển kinh nghiệm cũng có thể rơi vào bẫy khi áp dụng những đơn vị trong hệ thống đồng thời.

1. không phải là làm cho người trực

Nền tảng của bất kỳ một chiều đơn là một cấu trúc riêng để ngăn chặn sự phân loại bên ngoài. Nếu bộ xây dựng có thể truy cập (công cộng, bảo vệ gói), bất kỳ sợi nào cũng có thể tạo một tiến trình mới, phá vỡ hợp đồng đơn. Trong nhiều chương trình đọc, nó có thể xảy ra vô tình khi một lớp được tạo lại và khả năng cấu trúc được thay đổi ngẫu nhiên, hoặc khi lớp được phân loại (mặc dù lớp một công ty nhỏ) bị nản lòng. Luôn luôn tuyên bố cấu trúc riêng, và nếu bạn phải hỗ trợ hệ thống hỗ trợ bậc (ra), sử dụng một bộ cấu trúc được bảo vệ với hành vi cực đoan và tính năng cảnh giác cực đoan.

2. Không thể kiểm soát sự an toàn

Trong một môi trường chỉ đọc qua, một sự khởi đầu lười biếng đơn giản là tốt:

public class Singleton {
 private static Singleton instance;
 private Singleton() {}
 public static Singleton getInstance() {
 if (instance == null) {
 instance = new Singleton();
 }
 return instance;
 }
}

Nhưng trong một ứng dụng đa đọc, hai hay nhiều sợi có thể đồng thời đi vào ) kiểm tra trước khi có sợi nào tạo ra thể hiện. Mỗi sợi sau đó tiến hành tạo đối tượng ) riêng, vi phạm mẫu. Đây là một điều kiện [FLT: 0] cổ điển [FLT:] [FLT: 1) kết quả trong nhiều trường hợp và có thể dẫn tới trạng thái mâu thuẫn hay rò rỉ tài nguyên.

3 Dùng sự khởi đầu chậm chạp mà không cần sự đồng bộ thích hợp

Ngay cả những nhà phát triển nhận ra sự an toàn của sợi thường thêm vào sự đồng bộ hóa một cách ngây thơ.

public static synchronized Singleton getInstance() { ... }

Mọi cuộc gọi đạt được và giải phóng khóa, ngay cả sau khi đã tạo. Trong những trường hợp có ghi chú cao, giá trị này có thể giảm dần nghiêm trọng. Cách tiếp cận tốt hơn là dùng [FLT: 0] khóa [FLT: 1] (được phân tích dưới đây), nhưng ngay cả mô hình này có vết mắc lỗi nghiêm trọng nếu chưa được thực hiện đúng.

4. Quá trình đồng bộ quá tải

Sự đồng bộ hoá theo nhiều dạng: phương pháp, , khối có sẵn, , , v. v. Quá nhiều thời. Việc mở khóa thô thô (bằng tay) khi có sẵn sàng - dẫn đến sự tranh chấp không cần thiết. Trong một số ứng dụng kỹ thuật (v. d., hệ thống thực sự với ngân sách quản lý nghiêm ngặt, thậm chí vài trăm nano giây có thể được khóa trên cao. Mục tiêu là tối thiểu phần quan trọng trong khi vẫn còn bảo mật.

5. Bỏ qua từ khoá

Trong những ngôn ngữ như Java, C#, và C++ (với ], [FLT: 0] [FLT: 1] [FLT: 1]] là thiết yếu để có tầm nhìn chính xác trong mã đa đọc. Không có nó, bộ biên dịch hoặc CPU có thể sắp xếp lại chỉ dẫn, và thay đổi bởi một sợi có thể không hiển thị được. Trong việc kiểm tra hai kiểu khoá, không công bố một ví dụ như[FL11] có thể gây ra một sợi chỉ để xem một vật được xây dựng một phần, không thể đoán trước. Điều này là nguy hiểm nhất và nguy hiểm nhất.

Những thực hành tốt nhất cho việc tạo ra một đơn bổ sung an toàn

Để tránh những cạm bẫy này, hãy theo đuổi những chiến lược đã được chứng minh, mỗi phương pháp tiếp cận với sự an toàn, hiệu quả và đơn giản.

Một người trực và người giữ máy

Bất kể chiến lược ban đầu, người xây dựng phải là riêng. Trường hợp mẫu đơn cần được cất giữ trong một trường tĩnh. Đừng để lộ người xây dựng bằng bất cứ cách nào, và hãy xem xét việc làm cho lớp ở Java (hay trong C# để tránh tầng lớp phụ.

Dùng khối đồng bộ chỉ khi cần thiết

Để khởi tạo lười biếng, các mẫu khóa đã kiểm tra kép giảm sự đồng bộ hoá trên đầu:

public class Singleton {
 private static volatile Singleton instance;

 private Singleton() {}

 public static Singleton getInstance() {
 Singleton result = instance; // Local variable for performance
 if (result == null) {
 synchronized (Singleton.class) {
 result = instance;
 if (result == null) {
 instance = result = new Singleton();
 }
 }
 }
 return result;
 }
}

Trong mã này, việc kiểm tra bên ngoài khối đồng bộ tránh khóa khi có trường hợp đã có. Kiểm tra bên trong đảm bảo rằng chỉ một sợi tạo ra thể hiện. Từ khoá ngăn cản sự sắp xếp lại và đảm bảo nhiệm vụ hoàn toàn được hiển thị cho các sợi khác. Ghi chú rằng chúng ta lưu ý rằng chúng ta lưu ý rằng chúng ta lưu giữ các ví dụ trong biến số cục bộ. Hình này đúng trong Java (với mô hình bộ nhớ thích hợp) và hoạt động tương tự trong C# (FL: 18) và C [F: 18] bộ nhớ.

Khởi động nóng nảy

Nếu đơn vị này luôn cần thiết và sự sáng tạo là rẻ, sự khởi đầu háo hức là cách tiếp cận an toàn chỉ đơn giản nhất:

public class Singleton {
 private static final Singleton INSTANCE = new Singleton();

 private Singleton() {}

 public static Singleton getInstance() {
 return INSTANCE;
 }
}

Hạng tải vốn đã đồng bộ hoá bởi JVM, vì vậy không cần phải có sự phối hợp bổ sung. Tuy nhiên, điều này tạo ra thể hiện tại tải hạng, mà có thể không tốt trong hệ thống được đào tạo tài nguyên hoặc khi nào đơnton phụ thuộc vào cấu hình thời gian chạy mà chưa sẵn sàng.

Mẫu trạng thái tĩnh (bắt buộc- trên- demand)

Mô hình này kết hợp việc khởi tạo lười biếng với sự an toàn của sợi mà không cần đồng bộ hóa rõ ràng:

public class Singleton {
 private Singleton() {}

 private static class Holder {
 static final Singleton INSTANCE = new Singleton();
 }

 public static Singleton getInstance() {
 return Holder.INSTANCE;
 }
}

Lớp chỉ được nạp khi được gọi lần đầu tiên, và bảo đảm JVM sẽ an toàn xuất bản trường tĩnh trong khi tải. Đây được xem là giải pháp thanh lịch nhất cho các đơn Java.

Một mình vào hồ

Java ) khuyên dùng một bản in:

public enum Singleton {
 INSTANCE;

 // methods and fields
}

Hằng số vô định , và ngôn ngữ Java đảm bảo rằng các trường hợp enum chỉ được tạo ra một lần, thậm chí dưới các cuộc tấn công tiếp nối hoặc phản ánh. Đây là cả hai cách an toàn sợi và ngắn gọn. Tuy nhiên, các danh sách không thể mở rộng hạng (chỉ thực hiện giao diện), vì vậy chúng không thích hợp cho mọi trường hợp sử dụng.

Các mẫu tương đối trong C++ và C#

Trong C++, từ C++11, từ khi dùng từ cho đến khi dịch sang chữ Aleter ) (hình ảnh tĩnh) của MET:

Singleton& getInstance() {
 static Singleton instance;
 return instance;
}

Lớp học cung cấp một sự khởi đầu tự do trong chỉ an toàn:

public class Singleton {
 private static readonly Lazy<Singleton> _lazy =
 new Lazy<Singleton>(() => new Singleton());

 public static Singleton Instance => _lazy.Value;
}

Thử thách và xem xét trong các ứng dụng kỹ thuật

Trong các ứng dụng kỹ thuật, mẫu đơn tác động thường quản lý các nguồn tài nguyên chia sẻ như các trình điều khiển phần cứng, thiết lập, hồ chứa chỉ, hoặc dịch vụ ghi nhật ký. Kiểm tra các hoạt động thử nghiệm đa luồng cần thiết thiết thiết thiết kế cẩn thận. Hãy xem xét các cách sau:

  • Làm cho các hộp đơn có thể thử bằng cách cung cấp một cách để khởi động lại tiến trình (v. d., một phương pháp bảo vệ ] được dùng trong các cuộc thử nghiệm) hoặc bằng cách tiêm quan hệ phụ thuộc qua giao diện. Nhiều ứng dụng hiện đại tránh hoàn toàn các khung cài đặt phụ thuộc để quản lý xe đạp sự sống.
  • Mô phỏng phân tích trong thời gian thực hoặc hệ thống tần số cao: đo chi phí của sự đồng bộ hóa. Trong một số trường hợp, một giải pháp miễn phí (C#) hoặc (C++) có thể được hợp lệ.
  • Các hệ thống phân phối cần thiết các đơn kiện độc nhất trên mỗi tiến trình, không phải trên quá trình. Nếu bạn cần một tập hợp đơn phạm rộng, hãy sử dụng khả năng phối hợp bên ngoài (v. d., một cơ sở dữ liệu, Zoo Keeper, hoặc người dẫn đầu cuộc bầu cử).
  • Sự tái phát và sự lặp lại [FLT: 1] có thể bẻ gãy các bộton. Hãy dùng trong chuỗi Java, và ngăn chặn sự phản chiếu bằng cách ném một ngoại lệ vào bộ xây dựng nếu đã được thiết lập.

Kết luận

Mô hình Singleton vẫn còn là một công cụ giá trị trong hộp công cụ của phần mềm, nhưng việc thực hiện nó trong môi trường đọc đa phần đòi hỏi sự chú ý nghiêm ngặt đến chi tiết. Bằng cách hiểu và tránh những lỗi thông thường, như những người xây dựng không phải là phụ kiện, thiếu đồng bộ, sử dụng không đúng đắn, và sự thay đổi không đúng đắn -- những người phát triển có thể tạo ra những thiết bị phát triển mạnh mẽ, đơn giản hóa cao. Các mẫu khóa hai tầng, người giữ im lặng, và chỉ dựa trên một trong mỗi mô hình Java cung cấp một sự cân bằng vững chắc và hiệu quả. Trong công việc hiện đại, C++ và#

Để nghiên cứu thêm, hãy nhắc đến những nguồn tài nguyên sau:

Cuối cùng, việc thực hiện đơn giản nhất là cái mà đơn giản nhất cho yêu cầu của bạn. khi nghi ngờ, thích khởi đầu hay kiểu người giữ yên, và luôn viết các bài kiểm tra đồng thời để xác thực tính chính xác trong xung đột.