Bài viết đó cho biết:
"một chức năng có thể là reentrant, thread-safe, cả hai hoặc không."
Nó cũng cho biết:
"Các chức năng không lặp lại không an toàn chủ đề".
Tôi có thể thấy điều này có thể gây ra sự lộn xộn. Chúng có nghĩa là các hàm tiêu chuẩn được ghi nhận là không bắt buộc phải tái nhập cũng không bắt buộc phải an toàn luồng, đúng với các thư viện POSIX iirc (và POSIX khai báo nó cũng đúng với các thư viện ANSI/ISO, ISO có không có khái niệm về chủ đề và do đó không có khái niệm về chủ đề an toàn). Nói cách khác, "nếu một hàm nói rằng nó không phải là reentrant, thì nó đang nói nó cũng không an toàn". Đó không phải là sự cần thiết hợp lý, nó chỉ là một quy ước. Dưới đây là một số mã giả an toàn thread (tốt, có rất nhiều cơ hội cho các cuộc gọi lại để tạo các khóa chết do khóa đảo ngược, nhưng hãy giả sử tài liệu chứa đầy đủ thông tin để người dùng tránh điều đó) nhưng không tham gia lại .Đây là vụ để tăng bộ đếm toàn cầu, và thực hiện các cuộc gọi lại:
take_global_lock();
int i = get_global_counter();
do_callback(i);
set_global_counter(i+1);
release_global_lock();
Nếu gọi lại gọi thói quen này một lần nữa, kết quả là gọi lại khác, sau đó cả hai cấp độ của callback sẽ nhận được các thông số tương tự (có thể OK, tùy thuộc vào API), nhưng bộ đếm sẽ chỉ được tăng lên một lần (gần như chắc chắn không phải API bạn muốn, vì vậy nó sẽ bị cấm).
Đó là giả định khóa là đệ quy, tất nhiên. Nếu khóa không đệ quy, thì dĩ nhiên mã không phải là reentrant, vì lấy khóa lần thứ hai sẽ không hoạt động.
Dưới đây là một số mã giả mà là "một cách yếu ớt re-entrant" nhưng không thread-safe:
int i = get_global_counter();
do_callback(i);
set_global_counter(get_global_counter()+1);
Bây giờ nó là tốt để gọi hàm từ gọi lại, nhưng nó không phải là an toàn để gọi hàm đồng thời từ các chủ đề khác nhau. Nó cũng không an toàn để gọi nó từ một bộ xử lý tín hiệu, bởi vì sự ủy thác lại từ một bộ xử lý tín hiệu cũng có thể phá vỡ số đếm nếu tín hiệu xảy ra vào đúng thời điểm. Vì vậy, mã không được tái nhập theo định nghĩa đúng. Đây là một số mã được cho là hoàn toàn tái nhập (ngoại trừ tôi nghĩ tiêu chuẩn phân biệt giữa reentrant và 'không bị gián đoạn bởi tín hiệu', và tôi không chắc chắn nơi này rơi), nhưng vẫn không phải là thread- an toàn:
int i = get_global_counter();
do_callback(i);
disable_signals(); // and any other kind of interrupts on your system
set_global_counter(get_global_counter()+1);
restore_signal_state();
Trên ứng dụng đơn luồng, điều này là tốt, giả sử rằng hệ điều hành hỗ trợ vô hiệu hóa mọi thứ cần được tắt. Nó ngăn cản sự tái xuất hiện tại điểm quan trọng. Tùy thuộc vào cách tín hiệu bị vô hiệu hóa, có thể an toàn khi gọi từ trình xử lý tín hiệu, mặc dù trong ví dụ cụ thể này vẫn có sự cố của tham số được chuyển đến cuộc gọi lại giống nhau cho các cuộc gọi riêng biệt. Nó vẫn có thể đi sai đa luồng, mặc dù. Trong thực tế, không an toàn chủ đề thường ngụ ý không tái nhập, vì (không chính thức) bất cứ điều gì có thể đi sai do thread bị gián đoạn bởi bộ lập lịch và chức năng được gọi lại từ một chủ đề khác, cũng có thể đi sai nếu thread bị gián đoạn bởi một tín hiệu, và chức năng được gọi lại từ bộ xử lý tín hiệu. Nhưng sau đó các "sửa chữa" để ngăn chặn các tín hiệu (vô hiệu hóa chúng) là khác nhau từ "sửa chữa" để ngăn ngừa đồng thời (ổ khóa, thường). Đây là nguyên tắc tốt nhất.
Lưu ý rằng tôi đã ngụ ý các hình cầu ở đây, nhưng chính xác những cân nhắc tương tự sẽ áp dụng nếu hàm này lấy làm tham số con trỏ tới bộ đếm và khóa. Nó chỉ là các trường hợp khác nhau sẽ được thread-không an toàn hoặc không tái tham gia khi được gọi với cùng một tham số, chứ không phải là khi được gọi là ở tất cả.
Ví dụ thứ hai của bạn không có vẻ mang lại lợi ích cho tôi. Nếu sự thay đổi có thể bị gián đoạn để lại một trạng thái không nhất quán, và một tín hiệu xảy ra tại thời điểm đó, và xử lý cho tín hiệu đó gọi hàm, sau đó thông thường nó sẽ bùng nổ. Đó là vấn đề tái nhập, chứ không phải vấn đề an toàn chỉ. –
Bạn nói đúng - như bạn nói bên dưới, bạn cũng sẽ phải vô hiệu hóa các tín hiệu cho ví dụ sau để có hiệu quả. – ConcernedOfTunbridgeWells
@ConcernedOfTunbridgeWells, nếu một func sử dụng đống bên trong, có một cơ hội tốt mà func này không được tái nhập. Tại sao? – Alcott