Tôi tin tưởng điều này mạnh mẽ đến mức tôi nghĩ rằng các ngôn ngữ nên yêu cầu khai báo và truy cập vào các biến toàn cục để được bao bọc trong unsafe_and_evil
. Ví dụ
// foo.cpp unsafe_and_evil { int g_evil = 0; } void IncrementGlobal[int v] { unsafe_and_evil { g_evil += v; } }
Rust có từ khóa unsafe
. Điều này không đủ mạnh. Trạng thái toàn cầu có thể thay đổi vừa không an toàn vừa xấu xa. Các lập trình viên thêm các biến toàn cầu là xấu và nên cảm thấy tồi tệ
Điều này đã được nói về rất nhiều. Tôi sẽ không liệt kê tất cả các lý do. Tuy nhiên, đây là một vài điểm lấy cảm hứng từ các trường hợp tôi gặp phải gần đây
Thứ tự khởi tạo tĩnh thất bại là một khẩu súng lục C++ nổi tiếng. Cách khắc phục tốt nhất là "đừng làm thế". Thứ tự tắt máy cũng có vấn đề
Thư viện động Windows và Thư viện chia sẻ Linux hoạt động khác nhau. Windows .dll
là một hộp được đóng gói tốt với API mỏng. Linux ________ 8 mặc định là một API béo để hiển thị chi tiết triển khai cho các thư viện khác. Windows khiến việc chia sẻ các biến toàn cục trên .dlls
trở nên rất khó khăn. Linux cho phép và thậm chí khuyến khích các biến toàn cầu được chia sẻ. Đối phó với điều này là một nỗi đau cho dù bạn thích nền tảng nào hơn. Nếu bạn không có biến toàn cầu thì đó không phải là vấn đề
Các biến toàn cầu áp đặt các ràng buộc. Một thư viện dựa trên các biến toàn cục có thể buộc bạn phải sử dụng một singleton. Tôi đã sử dụng
// foo.cpp static int evil_1; int evil_2; void DoStuff[] { static int evil_3; thread_local int evil_4; } // foo.h extern int evil_5; int evil_6; struct Foo { static int evil_7; };0 để nhúng python vào một dự án. Do sử dụng các biến toàn cục, tôi bị giới hạn chỉ ở một "miền" Python duy nhất cho mỗi quy trình. Khi người dùng thứ hai muốn tính toán dài hạn, tùy chọn duy nhất là quy trình thứ hai. [Điều này khác với GIL. ]
Các biến toàn cầu kích hoạt các API xấu với hiệu suất kém. Đây là một ví dụ trong đó
// foo.cpp static int evil_1; int evil_2; void DoStuff[] { static int evil_3; thread_local int evil_4; } // foo.h extern int evil_5; int evil_6; struct Foo { static int evil_7; };1 khóa một mutex mỗi khi nó tìm nạp một
// foo.cpp static int evil_1; int evil_2; void DoStuff[] { static int evil_3; thread_local int evil_4; } // foo.h extern int evil_5; int evil_6; struct Foo { static int evil_7; };0. Nó gây ra sự chậm lại 500 lần do căng thẳng trong quá trình so sánh chuỗi đa luồng. C/C++
// foo.cpp static int evil_1; int evil_2; void DoStuff[] { static int evil_3; thread_local int evil_4; } // foo.h extern int evil_5; int evil_6; struct Foo { static int evil_7; };1 là một sự ghê tởm của thiết kế API tồi. Nếu nó không bao giờ sử dụng các biến toàn cầu thì nó sẽ bớt tệ hơn nhiều
Đây chỉ là một vài lý do. Chúng còn nhiều nữa. Trong vài năm qua, tôi đã sửa một số lỗi đáng ngạc nhiên mà nguyên nhân gốc rễ là do "các biến toàn cục"
Có một điều để nói rằng các biến toàn cầu là xấu xa. Nói cách khác, việc không sử dụng toàn cầu rõ ràng là tốt
Ví dụ yêu thích của tôi là cuộc nói chuyện GDC năm 2017 có tiêu đề Công nghệ phát lại trong 'Overwatch'. Kill Cam, Trò chơi và Điểm nổi bật
Overwatch là game bắn súng góc nhìn thứ nhất nhiều người chơi. Một tính năng rất được mong đợi trong các trò chơi này là "kill cam". Khi bạn chết, bạn sẽ được phát lại vài giây cuối cùng từ góc nhìn của kẻ giết người. Khi bạn bị bắn sau lưng, bạn có thể thấy nó xảy ra như thế nào
Đây là một tính năng đặc biệt phức tạp. Nó yêu cầu một số hình thức quay ngược thế giới và phát lại nó từ một góc nhìn khác
Mọi thứ trở nên phức tạp hơn khi quá trình phát lại có thể bị gián đoạn nhanh chóng. Người chơi có thể hủy phát lại vì thất vọng. Thậm chí phức tạp hơn, họ có thể được hồi sinh nhờ khả năng của đồng minh. Hồi sinh đặc biệt khó chịu vì nó có nghĩa là trạng thái thế giới phát lại không thể bỏ qua các sự kiện chơi trò chơi "trực tiếp"
Giải pháp của Blizzard cho vấn đề này là thiên tài. Họ tạo ra hai "miền" hoàn toàn rời rạc. "Miền phát lại" được điền khi chết và được hiển thị cho chế độ xem của khách hàng. Trong khi điều này đang xảy ra, "miền trực tiếp" vẫn đang xử lý các bản cập nhật. Trình kết xuất có thể chụp giữa hai bên bất cứ lúc nào
Hỗ trợ điều này yêu cầu loại bỏ tất cả các biến toàn cầu. Các miền phải hoàn toàn độc lập
Điều này thành công đến nỗi họ kết thúc với bốn miền. Trực tiếp, Phát lại, Sảnh chờ và dàn nhạc
Tôi nghĩ rằng giải pháp này là rất mát mẻ. Thật đơn giản và thanh lịch. Và nó không hoạt động nếu bạn có biến toàn cục hoặc biến đơn. Bài thuyết trình dài khoảng 50 phút và tôi thực sự khuyên bạn nên xem toàn bộ nội dung
Có phải các biến toàn cầu luôn xấu và không an toàn?
Ghi nhật ký theo dõi có thể có giá trị toàn cầu. Không hợp lý khi chuyển thông số ghi nhật ký cho mọi chức năng. Ghi nhật ký bình thường Tôi quan tâm nhiều hơn đến hàng rào. Nó có thể có giá trị toàn cầu. Tuy nhiên, việc đăng nhập toàn cầu cũng khiến tôi đau đớn. Vì vậy, tôi không bị thuyết phục
Có những trường hợp khác? . Có lẽ. Tôi chắc rằng mọi người sẽ cho tôi biết trong phần bình luận. Chúng tồn tại, nhưng chúng rất ít và cách xa nhau
Theo ý kiến của tôi, các biến toàn cầu là xấu xa và không an toàn và nên tránh. Sử dụng toàn cầu giới thiệu các lỗi, độ phức tạp và các ràng buộc không chính đáng. Tránh toàn cầu ít bị lỗi hơn, đơn giản hơn và linh hoạt hơn
Ngay cả các hệ thống đơn luồng cũng nên tránh toàn cầu. Bằng cách này, bạn có thể hỗ trợ hai hệ thống độc lập trên các luồng khác nhau. Điều này đặc biệt quan trọng trên Linux, nơi các thư viện dùng chung cũng thích chia sẻ toàn cầu hơn
Điều đáng buồn là toàn cầu thậm chí không thêm nhiều giá trị. Họ để bạn lười biếng. Toàn cầu cung cấp một lợi ích nhỏ bé, nhưng đi kèm với một cái giá lớn và dài hạn
Tôi sẽ không đi xa đến mức nói rằng các biến toàn cục không nên được hỗ trợ bởi các ngôn ngữ lập trình. tôi nghĩ về nó. gần rồi. Cuối cùng, tôi nghĩ rằng nó đáng để thoát hiểm. Tuy nhiên, lối thoát hiểm đó nên được đặt trong ngoặc đơn với unsafe_and_evil
để không khuyến khích sử dụng và buộc các lập trình viên phải đưa ra quyết định có ý thức
Có lẽ Carbon có thể bị thuyết phục để áp dụng unsafe_and_evil
. . ]
Cảm ơn vì đã đọc
Để rõ ràng, biến toàn cục là gì? . Tuy nhiên các khái niệm áp dụng cho bất kỳ ngôn ngữ
// foo.cpp static int evil_1; int evil_2; void DoStuff[] { static int evil_3; thread_local int evil_4; } // foo.h extern int evil_5; int evil_6; struct Foo { static int evil_7; };
Tất cả mọi người một trong những khai báo biến đó là ác. Tôi đã không bao gồm mọi hoán vị. Tất cả họ đều xấu
Các hằng số toàn cầu KHÔNG phải là xấu và bạn có thể tự do sử dụng chúng khi thích hợp
// All constant, all fine const int fine_1; constexpr int fine_2; constexpr const int fine_3;
Một cụm từ khác sẽ là "nhà nước toàn cầu có thể thay đổi là xấu xa". "Hằng" có phải là "biến" không? . Các ngôn ngữ khác nhau sử dụng các thuật ngữ khác nhau