Chính xác thì hàm reentrant là gì?


198

Hầu hết các các lần , định nghĩa về reentrance được trích dẫn từ Wikipedia :

Một chương trình hoặc thói quen máy tính được mô tả là reentrant nếu nó có thể được gọi lại một cách an toàn trước khi hoàn thành lệnh gọi trước đó (nghĩa là nó có thể được thực hiện đồng thời một cách an toàn). Để được reentrant, một chương trình máy tính hoặc thói quen:

  1. Không được giữ dữ liệu không cố định (hoặc toàn cầu).
  2. Không được trả lại địa chỉ cho dữ liệu không cố định (hoặc toàn cầu).
  3. Chỉ phải làm việc trên dữ liệu được cung cấp cho nó bởi người gọi.
  4. Không được dựa vào ổ khóa để tài nguyên đơn lẻ.
  5. Không được sửa đổi mã của chính nó (trừ khi thực thi trong bộ lưu trữ luồng duy nhất của chính nó)
  6. Không được gọi các chương trình hoặc chương trình máy tính không tái định cư.

Làm thế nào được xác định một cách an toàn ?

Nếu một chương trình có thể được thực hiện đồng thời một cách an toàn , điều đó có luôn có nghĩa là nó được phát lại không?

Chính xác thì chủ đề chung giữa sáu điểm được đề cập mà tôi nên ghi nhớ trong khi kiểm tra mã của mình để biết khả năng reentrant là gì?

Cũng thế,

  1. Có phải tất cả các hàm đệ quy reentrant?
  2. Có phải tất cả các chức năng an toàn chủ đề được reentrant?
  3. Có phải tất cả các hàm đệ quy và an toàn luồng được reentrant?

Trong khi viết câu hỏi này, có một điều xuất hiện trong đầu: Các thuật ngữ như reentencechủ đề an toàn tuyệt đối có nghĩa là chúng có các lỗi cụ thể không? Đối với, nếu họ không, câu hỏi này không có ý nghĩa lắm.


6
Thật ra, tôi không đồng ý với # 2 trong danh sách đầu tiên. Bạn có thể trả lại địa chỉ cho bất cứ điều gì bạn thích từ chức năng đăng ký lại - giới hạn là về những gì bạn làm với địa chỉ đó trong mã gọi.

2
@Neil Nhưng vì người viết hàm reentrant không thể kiểm soát những gì người gọi chắc chắn họ không được trả lại địa chỉ cho dữ liệu không cố định (hoặc toàn cầu) để nó được xác thực lại?
Robben_Ford_Fan_boy

2
@drelihan Không phải là trách nhiệm của người viết hàm BẤT K ((có trả lại hay không) để kiểm soát những gì người gọi làm với giá trị trả về. Họ chắc chắn nên nói những gì người gọi CÓ THỂ làm với nó, nhưng nếu người gọi chọn làm điều gì đó khác - may mắn khó khăn cho người gọi.

"An toàn chủ đề" là vô nghĩa trừ khi bạn cũng chỉ định chủ đề đang làm gì và hiệu quả mong đợi của việc làm của họ là gì. Nhưng có lẽ đó là một câu hỏi riêng biệt.

Tôi có nghĩa là an toàn, hành vi được xác định rõ ràng và xác định bất kể lịch trình.
AturSams

Câu trả lời:


191

1. Làm thế nào được xác định một cách an toàn ?

Về mặt ngữ nghĩa. Trong trường hợp này, đây không phải là một thuật ngữ khó xác định. Nó chỉ có nghĩa là "Bạn có thể làm điều đó, không có rủi ro".

2. Nếu một chương trình có thể được thực hiện đồng thời một cách an toàn, điều đó luôn có nghĩa là nó được reentrant?

Không.

Ví dụ: chúng ta có một hàm C ++ có cả khóa và gọi lại làm tham số:

#include <mutex>

typedef void (*callback)();
std::mutex m;

void foo(callback f)
{
    m.lock();
    // use the resource protected by the mutex

    if (f) {
        f();
    }

    // use the resource protected by the mutex
    m.unlock();
}

Một chức năng khác cũng có thể cần phải khóa cùng một mutex:

void bar()
{
    foo(nullptr);
}

Ngay từ cái nhìn đầu tiên, mọi thứ có vẻ ổn.

int main()
{
    foo(bar);
    return 0;
}

Nếu khóa trên mutex không được đệ quy, thì đây là điều sẽ xảy ra, trong luồng chính:

  1. mainsẽ gọi foo.
  2. foo sẽ có được khóa.
  3. foosẽ gọi bar, sẽ gọi foo.
  4. thứ 2 foosẽ cố gắng để có được khóa, thất bại và chờ đợi nó được phát hành.
  5. Bế tắc.
  6. Giáo sư…

Ok, tôi đã lừa dối, sử dụng điều gọi lại. Nhưng thật dễ để tưởng tượng những đoạn mã phức tạp hơn có tác dụng tương tự.

3. Chính xác thì chủ đề chung giữa sáu điểm được đề cập mà tôi nên ghi nhớ trong khi kiểm tra mã của mình để biết khả năng reentrant là gì?

Bạn có thể ngửi thấy một vấn đề nếu chức năng của bạn có / cấp quyền truy cập vào tài nguyên liên tục có thể sửa đổi hoặc có / cung cấp quyền truy cập vào một chức năng có mùi .

( Ok, 99% mã của chúng tôi sẽ có mùi, sau đó Xem phần cuối để xử lý điều đó )

Vì vậy, nghiên cứu mã của bạn, một trong những điểm đó sẽ cảnh báo bạn:

  1. Hàm này có một trạng thái (tức là truy cập một biến toàn cục hoặc thậm chí là một biến thành viên lớp)
  2. Hàm này có thể được gọi bởi nhiều luồng hoặc có thể xuất hiện hai lần trong ngăn xếp trong khi quá trình đang thực thi (tức là hàm có thể tự gọi, trực tiếp hoặc gián tiếp). Chức năng nhận cuộc gọi lại như tham số mùi rất nhiều.

Lưu ý rằng không reentrancy là virus: Một chức năng có thể gọi là chức năng không reentrant có thể có thể được coi là reentrant.

Cũng lưu ý rằng các phương thức C ++ có mùi vì chúng có quyền truy cập this, vì vậy bạn nên nghiên cứu mã để chắc chắn rằng chúng không có tương tác hài hước.

4.1. Có phải tất cả các hàm đệ quy reentrant?

Không.

Trong các trường hợp đa luồng, một hàm đệ quy truy cập vào một tài nguyên được chia sẻ có thể được gọi bởi nhiều luồng cùng một lúc, dẫn đến dữ liệu xấu / bị hỏng.

Trong các trường hợp đơn lẻ, một hàm đệ quy có thể sử dụng hàm không reentrant (như khét tiếng strtok) hoặc sử dụng dữ liệu toàn cầu mà không xử lý thực tế dữ liệu đã được sử dụng. Vì vậy, chức năng của bạn là đệ quy vì nó tự gọi trực tiếp hoặc gián tiếp, nhưng nó vẫn có thể được đệ quy-không an toàn .

4.2. Có phải tất cả các chức năng an toàn chủ đề được reentrant?

Trong ví dụ trên, tôi đã chỉ ra làm thế nào một chức năng chủ đề rõ ràng không được phát lại. OK, tôi đã gian lận vì tham số gọi lại. Nhưng sau đó, có nhiều cách để bế tắc một luồng bằng cách lấy nó hai lần một khóa không đệ quy.

4.3. Có phải tất cả các hàm đệ quy và an toàn luồng được reentrant?

Tôi sẽ nói "có" nếu bằng "đệ quy" bạn có nghĩa là "an toàn đệ quy".

Nếu bạn có thể đảm bảo rằng một hàm có thể được gọi đồng thời bởi nhiều luồng và có thể gọi chính nó, trực tiếp hoặc gián tiếp, mà không gặp vấn đề gì, thì nó sẽ được phát lại.

Vấn đề là đánh giá sự đảm bảo này ^ ^ ^

5. Các thuật ngữ như reentence và thread safe có tuyệt đối không, tức là chúng có các định nghĩa cụ thể cố định không?

Tôi tin rằng họ làm, nhưng sau đó, việc đánh giá một chức năng là an toàn theo luồng hoặc reentrant có thể khó khăn. Đây là lý do tại sao tôi sử dụng thuật ngữ mùi ở trên: Bạn có thể tìm thấy một hàm không được reentrant, nhưng có thể khó chắc chắn một đoạn mã phức tạp được reentrant

6. Một ví dụ

Giả sử bạn có một đối tượng, với một phương thức cần sử dụng tài nguyên:

struct MyStruct
{
    P * p;

    void foo()
    {
        if (this->p == nullptr)
        {
            this->p = new P();
        }

        // lots of code, some using this->p

        if (this->p != nullptr)
        {
            delete this->p;
            this->p = nullptr;
        }
    }
};

Vấn đề đầu tiên là nếu bằng cách nào đó, hàm này được gọi là đệ quy (nghĩa là hàm này tự gọi, trực tiếp hoặc gián tiếp), mã có thể sẽ bị sập, bởi vì this->psẽ bị xóa vào cuối cuộc gọi cuối cùng và vẫn có thể được sử dụng trước khi kết thúc của cuộc gọi đầu tiên.

Vì vậy, mã này không an toàn đệ quy .

Chúng tôi có thể sử dụng một bộ đếm tham chiếu để sửa lỗi này:

struct MyStruct
{
    size_t c;
    P * p;

    void foo()
    {
        if (c == 0)
        {
            this->p = new P();
        }

        ++c;
        // lots of code, some using this->p
        --c;

        if (c == 0)
        {
            delete this->p;
            this->p = nullptr;
        }
    }
};

Bằng cách này, mã trở nên an toàn đệ quy Nhưng nó vẫn không được phát hành lại do các vấn đề đa luồng: Chúng ta phải chắc chắn rằng các sửa đổi cpsẽ được thực hiện một cách nguyên tử, sử dụng một mutex đệ quy (không phải tất cả các mutex đều được đệ quy):

#include <mutex>

struct MyStruct
{
    std::recursive_mutex m;
    size_t c;
    P * p;

    void foo()
    {
        m.lock();

        if (c == 0)
        {
            this->p = new P();
        }

        ++c;
        m.unlock();
        // lots of code, some using this->p
        m.lock();
        --c;

        if (c == 0)
        {
            delete this->p;
            this->p = nullptr;
        }

        m.unlock();
    }
};

Và tất nhiên, tất cả điều này giả định rằng lots of codechính nó được reentrant, bao gồm cả việc sử dụng p.

Và đoạn mã trên thậm chí không an toàn từ xa , nhưng đây là một câu chuyện khác.

7. Hey 99% mã của chúng tôi không được nhận lại!

Nó khá đúng với mã spaghetti. Nhưng nếu bạn phân vùng chính xác mã của mình, bạn sẽ tránh được các vấn đề reentrancy.

7.1. Đảm bảo tất cả các chức năng không có trạng thái

Họ chỉ phải sử dụng các tham số, biến cục bộ của riêng mình, các hàm khác không có trạng thái và trả về các bản sao của dữ liệu nếu chúng trả về.

7.2. Hãy chắc chắn rằng đối tượng của bạn là "đệ quy an toàn"

Một phương thức đối tượng có quyền truy cập this, vì vậy nó chia sẻ trạng thái với tất cả các phương thức của cùng một thể hiện của đối tượng.

Vì vậy, hãy chắc chắn rằng đối tượng có thể được sử dụng tại một điểm trong ngăn xếp (tức là gọi phương thức A), và sau đó, tại một điểm khác (tức là gọi phương thức B), mà không làm hỏng toàn bộ đối tượng. Thiết kế đối tượng của bạn để đảm bảo rằng khi thoát khỏi một phương thức, đối tượng đó ổn định và chính xác (không có con trỏ lơ lửng, không có các biến thành viên mâu thuẫn, v.v.).

7.3. Đảm bảo tất cả các đối tượng của bạn được đóng gói chính xác

Không ai khác có quyền truy cập vào dữ liệu nội bộ của họ:

    // bad
    int & MyObject::getCounter()
    {
        return this->counter;
    }

    // good
    int MyObject::getCounter()
    {
        return this->counter;
    }

    // good, too
    void MyObject::getCounter(int & p_counter)
    {
        p_counter = this->counter;
    }

Ngay cả việc trả về một tham chiếu const cũng có thể nguy hiểm nếu người dùng lấy địa chỉ của dữ liệu, vì một số phần khác của mã có thể sửa đổi nó mà không cần mã giữ tham chiếu const được nói.

7.4. Đảm bảo người dùng biết đối tượng của bạn không an toàn cho chuỗi

Do đó, người dùng có trách nhiệm sử dụng mutexes để sử dụng một đối tượng được chia sẻ giữa các luồng.

Các đối tượng từ STL được thiết kế để không an toàn cho luồng (vì vấn đề về hiệu năng) và do đó, nếu người dùng muốn chia sẻ std::stringgiữa hai luồng, người dùng phải bảo vệ quyền truy cập của nó bằng các nguyên hàm đồng thời;

7.5. Đảm bảo mã an toàn luồng của bạn là đệ quy an toàn

Điều này có nghĩa là sử dụng các mutex đệ quy nếu bạn tin rằng cùng một tài nguyên có thể được sử dụng hai lần bởi cùng một luồng.


1
Để phân biệt một chút, tôi thực sự nghĩ rằng trong trường hợp này "an toàn" được xác định - điều đó có nghĩa là hàm sẽ chỉ hoạt động trên các biến được cung cấp - tức là, nó là viết tắt cho trích dẫn định nghĩa bên dưới nó. Và điểm là điều này có thể không ngụ ý các ý tưởng khác về an toàn.
Joe Soul-bringer

Bạn đã bỏ lỡ trong mutex trong ví dụ đầu tiên?
gièm pha

@paercebal: ví dụ của bạn sai. Bạn thực sự không cần phải bận tâm với cuộc gọi lại, một đệ quy đơn giản sẽ có cùng một vấn đề nếu có, tuy nhiên vấn đề duy nhất là bạn quên nói chính xác nơi khóa được phân bổ.
Yttrill

3
@Yttrill: Tôi giả sử bạn đang nói về ví dụ đầu tiên. Tôi đã sử dụng "gọi lại" bởi vì, về bản chất, một cuộc gọi lại có mùi. Tất nhiên, một hàm đệ quy sẽ có cùng một vấn đề, nhưng thông thường, người ta có thể dễ dàng phân tích một hàm và bản chất đệ quy của nó, do đó, phát hiện xem nó có tái phát hay không cũng có thể đệ quy. Mặt khác, hàm gọi lại có nghĩa là tác giả của hàm gọi lại gọi lại không có thông tin gì về việc gọi lại làm gì, vì vậy tác giả này có thể thấy khó khăn để đảm bảo chức năng của mình được nối lại. Đây là khó khăn này tôi muốn thể hiện.
paercebal

1
@Gab: Tôi đã sửa ví dụ đầu tiên. Cảm ơn! Một trình xử lý tín hiệu sẽ đi kèm với các vấn đề của riêng nó, khác với sự tái lập, như thường lệ, khi tín hiệu được đưa ra, bạn thực sự không thể làm gì ngoài việc thay đổi một biến toàn cục được khai báo cụ thể.
paercebal

21

"An toàn" được định nghĩa chính xác như ý nghĩa thông thường ra lệnh - nó có nghĩa là "làm việc của mình một cách chính xác mà không can thiệp vào những việc khác". Sáu điểm bạn trích dẫn khá rõ ràng thể hiện các yêu cầu để đạt được điều đó.

Câu trả lời cho 3 câu hỏi của bạn là 3 × "không".


Có phải tất cả các hàm đệ quy reentrant?

KHÔNG!

Hai lệnh gọi đồng thời của một hàm đệ quy có thể dễ dàng kết nối lẫn nhau, nếu chúng truy cập cùng một dữ liệu toàn cầu / tĩnh, chẳng hạn.


Có phải tất cả các chức năng an toàn chủ đề được reentrant?

KHÔNG!

Một chức năng là an toàn luồng nếu nó không hoạt động nếu được gọi đồng thời. Nhưng điều này có thể đạt được, ví dụ bằng cách sử dụng một mutex để chặn việc thực hiện lệnh gọi thứ hai cho đến khi kết thúc đầu tiên, do đó chỉ có một lệnh gọi hoạt động tại một thời điểm. Reentrancy có nghĩa là thực hiện đồng thời mà không can thiệp vào các yêu cầu khác .


Có phải tất cả các hàm đệ quy và an toàn luồng được reentrant?

KHÔNG!

Xem ở trên.


10

Chủ đề chung:

Là hành vi được xác định rõ nếu thói quen được gọi trong khi nó bị gián đoạn?

Nếu bạn có một chức năng như thế này:

int add( int a , int b ) {
  return a + b;
}

Sau đó, nó không phụ thuộc vào bất kỳ trạng thái bên ngoài. Các hành vi được xác định rõ.

Nếu bạn có một chức năng như thế này:

int add_to_global( int a ) {
  return gValue += a;
}

Kết quả không được xác định rõ trên nhiều chủ đề. Thông tin có thể bị mất nếu thời gian chỉ là sai.

Dạng đơn giản nhất của hàm reentrant là một cái gì đó chỉ hoạt động trên các đối số được truyền và các giá trị không đổi. Bất cứ điều gì khác cần xử lý đặc biệt hoặc, thường, không được nhận lại. Và tất nhiên các đối số không được tham chiếu toàn cầu có thể thay đổi.


7

Bây giờ tôi phải giải thích về nhận xét trước đây của tôi. @paercebal trả lời không chính xác. Trong mã ví dụ, không ai nhận thấy rằng mutex mà được coi là tham số không thực sự được truyền vào?

Tôi tranh luận về kết luận, tôi khẳng định: để một chức năng được an toàn khi có sự đồng thời thì nó phải được đăng ký lại. Do đó, an toàn đồng thời (thường được viết là an toàn chủ đề) ngụ ý tái đăng ký.

Cả chủ đề an toàn và người đăng ký lại đều không có bất cứ điều gì để nói về các đối số: chúng ta đang nói về việc thực thi đồng thời của hàm, vẫn có thể không an toàn nếu sử dụng các tham số không phù hợp.

Ví dụ, memcpy () là chủ đề an toàn và tái đăng ký (thường). Rõ ràng là nó sẽ không hoạt động như mong đợi nếu được gọi với các con trỏ tới cùng các mục tiêu từ hai luồng khác nhau. Đó là điểm của định nghĩa SGI, đặt onus trên máy khách để đảm bảo quyền truy cập vào cùng cấu trúc dữ liệu được máy khách đồng bộ hóa.

Điều quan trọng là phải hiểu rằng nói chung là vô nghĩa khi có hoạt động an toàn luồng bao gồm các tham số. Nếu bạn đã thực hiện bất kỳ lập trình cơ sở dữ liệu, bạn sẽ hiểu. Khái niệm "nguyên tử" và có thể được bảo vệ bởi một mutex hoặc một số kỹ thuật khác nhất thiết phải là một khái niệm người dùng: xử lý một giao dịch trên cơ sở dữ liệu có thể yêu cầu nhiều sửa đổi không bị gián đoạn. Ai có thể nói cái nào cần được giữ đồng bộ nhưng lập trình khách?

Vấn đề là "tham nhũng" không cần phải làm rối bộ nhớ trên máy tính của bạn bằng cách ghi không xác thực: tham nhũng vẫn có thể xảy ra ngay cả khi tất cả các hoạt động riêng lẻ được nối tiếp. Nó diễn ra rằng khi bạn hỏi liệu một hàm có an toàn theo luồng hay được đăng ký lại hay không, câu hỏi có nghĩa là cho tất cả các đối số được phân tách thích hợp: sử dụng các đối số được ghép không tạo thành một ví dụ ngược lại.

Có rất nhiều hệ thống lập trình ngoài kia: Ocaml là một, và tôi nghĩ Python cũng vậy, có rất nhiều mã không được gửi lại trong đó, nhưng sử dụng khóa toàn cầu để xen kẽ các luồng. Các hệ thống này không được đăng ký lại và chúng không an toàn theo luồng hoặc an toàn đồng thời, chúng hoạt động an toàn đơn giản vì chúng ngăn chặn sự tương tranh trên toàn cầu.

Một ví dụ điển hình là malloc. Nó không được đăng ký lại và không an toàn cho chuỗi. Điều này là do nó phải truy cập vào một tài nguyên toàn cầu (heap). Sử dụng khóa không làm cho nó an toàn: nó chắc chắn không được đăng ký lại. Nếu giao diện cho malloc được thiết kế đúng cách, có thể làm cho nó được đăng ký lại và an toàn cho chuỗi:

malloc(heap*, size_t);

Bây giờ nó có thể an toàn vì nó chuyển trách nhiệm nối tiếp truy cập được chia sẻ thành một đống cho khách hàng. Đặc biệt không có công việc nào được yêu cầu nếu có các đối tượng heap riêng. Nếu một đống chung được sử dụng, máy khách phải truy cập nối tiếp. Sử dụng khóa bên trong chức năng là không đủ: chỉ cần xem xét một malloc khóa một đống * và sau đó một tín hiệu xuất hiện và gọi malloc trên cùng một con trỏ: bế tắc: tín hiệu không thể tiếp tục và khách hàng không thể vì nó bị gián đoạn.

Nói chung, các khóa không làm cho mọi thứ trở nên an toàn .. chúng thực sự phá hủy sự an toàn bằng cách cố gắng không phù hợp để quản lý tài nguyên thuộc sở hữu của khách hàng. Việc khóa phải được thực hiện bởi nhà sản xuất đối tượng, đó là mã duy nhất biết có bao nhiêu đối tượng được tạo và cách chúng sẽ được sử dụng.


"Do đó đồng thời an toàn (thường được viết bằng văn bản an toàn) ngụ ý tái đăng ký." Điều này mâu thuẫn với ví dụ Wikipedia "An toàn chủ đề nhưng không tái phát hành" .
Maggyero

3

"Chủ đề chung" (ý định chơi chữ!?) Trong số các điểm được liệt kê là hàm không được làm bất cứ điều gì có thể ảnh hưởng đến hành vi của bất kỳ lệnh gọi đệ quy hoặc đồng thời nào đến cùng hàm.

Vì vậy, ví dụ dữ liệu tĩnh là một vấn đề vì nó được sở hữu bởi tất cả các luồng; nếu một cuộc gọi sửa đổi một biến tĩnh, tất cả các luồng sử dụng dữ liệu đã sửa đổi, do đó ảnh hưởng đến hành vi của chúng. Mã tự sửa đổi (mặc dù hiếm khi gặp phải, và trong một số trường hợp bị ngăn chặn) sẽ là một vấn đề, bởi vì mặc dù có nhiều luồng, chỉ có một bản sao của mã; mã là dữ liệu tĩnh thiết yếu quá.

Về cơ bản để được đăng ký lại, mỗi luồng phải có thể sử dụng hàm như thể nó là người dùng duy nhất và đó không phải là trường hợp nếu một luồng có thể ảnh hưởng đến hành vi của người khác theo cách không xác định. Chủ yếu điều này liên quan đến mỗi luồng có dữ liệu riêng biệt hoặc không đổi mà hàm hoạt động.

Tất cả những gì đã nói, điểm (1) không nhất thiết là đúng; ví dụ, bạn có thể hợp pháp và theo thiết kế sử dụng một biến tĩnh để giữ lại số đệ quy để bảo vệ chống lại đệ quy quá mức hoặc lập hồ sơ thuật toán.

Một chức năng an toàn chủ đề không cần phải được reentrant; nó có thể đạt được sự an toàn của luồng bằng cách đặc biệt ngăn chặn reentrancy bằng khóa và điểm (6) nói rằng chức năng như vậy không được cấp lại. Về điểm (6), một hàm gọi hàm an toàn luồng mà khóa không an toàn để sử dụng trong đệ quy (nó sẽ khóa chết), và do đó không được cho là được cấp lại, mặc dù vậy dù sao nó cũng có thể an toàn cho đồng thời, và vẫn sẽ được đăng ký lại theo nghĩa là nhiều luồng có thể có các bộ đếm chương trình của chúng trong một chức năng như vậy đồng thời (không phải với vùng bị khóa). Có thể điều này giúp phân biệt sự an toàn của luồng với sự tái sinh (hoặc có thể làm tăng thêm sự nhầm lẫn của bạn!).


1

Các câu trả lời cho câu hỏi "Ngoài ra" của bạn là "Không", "Không" và "Không". Chỉ vì một hàm được đệ quy và / hoặc luồng an toàn, nó không làm cho nó được cấp lại.

Mỗi loại chức năng này có thể thất bại trên tất cả các điểm bạn trích dẫn. (Mặc dù tôi không chắc chắn 100% về điểm 5).


1

Các thuật ngữ "An toàn chủ đề" và "đăng ký lại" chỉ có nghĩa và chính xác những gì định nghĩa của họ nói. "An toàn" trong ngữ cảnh này chỉ có nghĩa là những gì định nghĩa bạn trích dẫn bên dưới nó nói.

"An toàn" ở đây chắc chắn không có nghĩa là an toàn theo nghĩa rộng hơn là gọi một hàm nhất định trong ngữ cảnh cụ thể sẽ không hoàn toàn làm hỏng ứng dụng của bạn. Nhìn chung, một chức năng có thể tạo ra hiệu ứng mong muốn trong ứng dụng đa luồng của bạn nhưng không đủ điều kiện là người đăng ký lại hoặc an toàn luồng theo định nghĩa. Ngược lại, bạn có thể gọi các hàm đăng ký lại theo cách sẽ tạo ra nhiều hiệu ứng không mong muốn, bất ngờ và / hoặc không thể đoán trước trong ứng dụng đa luồng của bạn.

Hàm đệ quy có thể là bất cứ điều gì và Re-entrant có định nghĩa mạnh hơn an toàn luồng nên các câu trả lời cho các câu hỏi được đánh số của bạn đều không có.

Đọc định nghĩa của người đăng ký lại, người ta có thể tóm tắt nó có nghĩa là một chức năng sẽ không sửa đổi bất kỳ điều gì ngoài những gì bạn gọi nó để sửa đổi. Nhưng bạn không nên chỉ dựa vào bản tóm tắt.

Lập trình đa luồng chỉ là cực kỳ khó khăn trong trường hợp chung. Biết phần nào của người đăng ký lại mã chỉ là một phần của thử thách này. An toàn chủ đề không phải là phụ gia. Thay vì cố gắng ghép các chức năng lại với nhau, tốt hơn là sử dụng một mẫu thiết kế an toàn chủ đề tổng thể và sử dụng mẫu này để hướng dẫn bạn sử dụng mọi luồng và tài nguyên được chia sẻ trong chương trình của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.