Điều gì làm cho một phương pháp chủ đề an toàn? Các quy tắc là gì?


156

Có các quy tắc / hướng dẫn chung cho những gì làm cho một luồng phương thức an toàn không? Tôi hiểu rằng có lẽ có một triệu tình huống một lần, nhưng nói chung thì sao? Nó có đơn giản không?

  1. Nếu một phương thức chỉ truy cập các biến cục bộ, thì nó sẽ an toàn.

Là nó? Điều đó có áp dụng cho các phương thức tĩnh không?

Một câu trả lời, được cung cấp bởi @Cybis, là:

Các biến cục bộ không thể được chia sẻ giữa các luồng vì mỗi luồng có ngăn xếp riêng.

Đó có phải là trường hợp cho các phương thức tĩnh không?

Nếu một phương thức được truyền vào một đối tượng tham chiếu, điều đó có phá vỡ sự an toàn của luồng không? Tôi đã thực hiện một số nghiên cứu, và có rất nhiều trường hợp về một số trường hợp nhất định, nhưng tôi hy vọng có thể xác định, bằng cách chỉ sử dụng một vài quy tắc, hướng dẫn để tuân theo để đảm bảo phương pháp là an toàn.

Vì vậy, tôi đoán câu hỏi cuối cùng của tôi là: "Có một danh sách ngắn các quy tắc xác định phương thức an toàn luồng không? Nếu vậy, chúng là gì?"

EDIT
Rất nhiều điểm tốt đã được thực hiện ở đây. Tôi nghĩ rằng câu trả lời thực sự cho câu hỏi này là: "Không có quy tắc đơn giản nào để đảm bảo an toàn cho luồng." Mát mẻ. Khỏe. Nhưng nói chung tôi nghĩ rằng câu trả lời được chấp nhận cung cấp một bản tóm tắt tốt, ngắn. Luôn có ngoại lệ. Như chỉ thị. Tôi có thể sống với điều đó.


59
Bạn sẽ không truy cập các biến cũng được truy cập bởi các chủ đề khác mà không có mề đay.
Hans Passant

4
Hanth pathant hath biến thành một igor!
Martin James

3
Ngoài ra .. 'Bạn sẽ không truy cập vào các biến cũng được truy cập bởi các luồng khác mà không có mề đay' - không hiểu rõ lắm nếu giá trị đọc không phải là mới nhất hoặc thực sự không chính xác.
Martin James

Đây là một blog hay của Eric để đưa bạn vào một cơn lốc.
RBT

Câu trả lời:


139

Nếu một phương thức (thể hiện hoặc tĩnh) chỉ tham chiếu các biến trong phạm vi của phương thức đó thì đó là luồng an toàn vì mỗi luồng có ngăn xếp riêng:

Trong trường hợp này, nhiều luồng có thể gọi ThreadSafeMethodđồng thời mà không có vấn đề.

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number; // each thread will have its own variable for number.
        number = parameter1.Length;
        return number;
    }
}

Điều này cũng đúng nếu phương thức gọi phương thức lớp khác chỉ tham chiếu các biến có phạm vi cục bộ:

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number;
        number = this.GetLength(parameter1);
        return number;
    }

    private int GetLength(string value)
    {
        int length = value.Length;
        return length;
    }
}

Nếu một phương thức truy cập bất kỳ thuộc tính (trạng thái đối tượng) hoặc trường (thể hiện hoặc tĩnh) thì bạn cần sử dụng các khóa để đảm bảo rằng các giá trị không bị sửa đổi bởi một luồng khác.

public class Thing
{
    private string someValue; // all threads will read and write to this same field value

    public int NonThreadSafeMethod(string parameter1)
    {
        this.someValue = parameter1;

        int number;

        // Since access to someValue is not synchronised by the class, a separate thread
        // could have changed its value between this thread setting its value at the start 
        // of the method and this line reading its value.
        number = this.someValue.Length;
        return number;
    }
}

Bạn nên lưu ý rằng bất kỳ tham số nào được truyền vào phương thức không phải là cấu trúc hoặc bất biến có thể bị thay đổi bởi một luồng khác ngoài phạm vi của phương thức.

Để đảm bảo đồng thời thích hợp, bạn cần sử dụng khóa.

để biết thêm thông tin, xem tham chiếu khóa C # tham chiếuReadWriterLockSlim .

Khóa chủ yếu hữu ích để cung cấp một chức năng tại một thời điểm,
ReadWriterLockSlimrất hữu ích nếu bạn cần nhiều người đọc và người viết đơn.


15
Trong ví dụ thứ ba private string someValue;là không staticvì vậy mỗi trường hợp sẽ nhận được một bản sao riêng của biến đó. Vì vậy, bạn có thể vui lòng giải thích làm thế nào đây không phải là chủ đề an toàn?
Bharadwaj

29
@Bharadwaj nếu có một phiên bản của Thinglớp được truy cập bởi nhiều luồng
Trevor Pilley

111

Nếu một phương thức chỉ truy cập các biến cục bộ, thì nó sẽ an toàn. Là nó?

Hoàn toàn không. Bạn có thể viết một chương trình chỉ với một biến cục bộ duy nhất được truy cập từ một luồng duy nhất không phải là luồng an toàn:

https://stackoverflow.com/a/8883117/88656

Điều đó có áp dụng cho các phương thức tĩnh không?

Tuyệt đối không.

Một câu trả lời, được cung cấp bởi @Cybis, là: "Các biến cục bộ không thể được chia sẻ giữa các luồng vì mỗi luồng có ngăn xếp riêng."

Tuyệt đối không. Đặc điểm phân biệt của một biến cục bộ là nó chỉ được nhìn thấy từ trong phạm vi cục bộ , chứ không phải nó được phân bổ trên nhóm tạm thời . Hoàn toàn hợp pháp và có thể truy cập cùng một biến cục bộ từ hai luồng khác nhau. Bạn có thể làm như vậy bằng cách sử dụng các phương thức ẩn danh, lambdas, khối lặp hoặc phương thức async.

Đó có phải là trường hợp cho các phương thức tĩnh không?

Tuyệt đối không.

Nếu một phương thức được truyền vào một đối tượng tham chiếu, điều đó có phá vỡ sự an toàn của luồng không?

Có lẽ.

Tôi đã thực hiện một số nghiên cứu, và có rất nhiều trường hợp về một số trường hợp nhất định, nhưng tôi hy vọng có thể xác định, bằng cách chỉ sử dụng một vài quy tắc, hướng dẫn để tuân theo để đảm bảo phương pháp là an toàn.

Bạn sẽ phải học cách sống với sự thất vọng. Đây là một chủ đề rất khó.

Vì vậy, tôi đoán câu hỏi cuối cùng của tôi là: "Có một danh sách ngắn các quy tắc xác định phương pháp an toàn luồng không?

Không. Như bạn đã thấy từ ví dụ của tôi trước đó, một phương thức rỗng có thể không an toàn cho luồng . Bạn cũng có thể hỏi "có một danh sách ngắn các quy tắc đảm bảo phương thức là chính xác không ". Không có. An toàn chủ đề không gì khác hơn là một loại chính xác cực kỳ phức tạp.

Hơn nữa, thực tế là bạn đang đặt câu hỏi cho thấy sự hiểu lầm cơ bản của bạn về an toàn luồng. An toàn chủ đề là một toàn cầu , không phải là một tài sản địa phương của một chương trình. Lý do tại sao rất khó để có được đúng là vì bạn phải có kiến ​​thức đầy đủ về hành vi phân luồng của toàn bộ chương trình để đảm bảo an toàn.

Một lần nữa, hãy nhìn vào ví dụ của tôi: mọi phương pháp đều tầm thường . Đó là cách các phương thức tương tác với nhau ở mức độ "toàn cầu" làm cho chương trình bế tắc. Bạn không thể xem mọi phương pháp và kiểm tra xem nó có "an toàn" không và sau đó mong đợi rằng toàn bộ chương trình là an toàn, nhiều hơn bạn có thể kết luận rằng vì ngôi nhà của bạn được làm bằng gạch không rỗng 100% mà ngôi nhà cũng được không rỗng. Độ rỗng của một ngôi nhà là một tài sản toàn cầu của toàn bộ, không phải là tổng hợp các thuộc tính của các bộ phận của nó.


14
Tuyên bố cốt lõi: An toàn luồng là toàn cầu, không phải là tài sản cục bộ của chương trình.
Greg D

5
@BobHorn: class C { public static Func<int> getter; public static Action<int> setter; public static void M() { int x = 0; getter = ()=>x; setter = y=>{x=y;};} } Gọi M (), sau đó gọi C.getter và C.setter trên hai luồng khác nhau. Biến cục bộ bây giờ có thể được ghi và đọc từ trên hai luồng khác nhau mặc dù nó là cục bộ. Một lần nữa: đặc điểm xác định của một biến cục bộ là nó là cục bộ , không phải là nó nằm trên ngăn xếp của luồng .
Eric Lippert

3
@BobHorn: Tôi nghĩ rằng nó có liên quan nhiều hơn đến việc hiểu các công cụ của chúng tôi ở cấp độ để chúng tôi có thể nói về chúng với thẩm quyền và kiến ​​thức. Ví dụ, câu hỏi ban đầu phản bội sự thiếu hiểu biết về biến địa phương là gì. Lỗi này khá phổ biến, nhưng thực tế là nó là một lỗi và cần được sửa chữa. Định nghĩa của một biến cục bộ là khá chính xác và cần được xử lý tương ứng. :) Đây không phải là một câu trả lời cho "những người" không có trường hợp bệnh lý tồn tại. Đây là một sự làm rõ để bạn có thể hiểu những gì bạn thực sự hỏi. :)
Greg D

7
@EricLippert: Tài liệu MSDN cho nhiều lớp tuyên bố rằng các thành viên 'Public static (Shared in Visual Basic) thuộc loại này là an toàn cho luồng. Bất kỳ thành viên cá thể nào không được đảm bảo là chủ đề an toàn. ' hoặc đôi khi 'Loại này là chủ đề an toàn.' (ví dụ Chuỗi), làm thế nào những đảm bảo này có thể được thực hiện khi an toàn luồng là mối quan tâm toàn cầu?
Mike Zboray

3
@mikez: Câu hỏi hay. Các phương thức đó không làm thay đổi bất kỳ trạng thái nào hoặc khi thực hiện, chúng sẽ biến đổi trạng thái một cách an toàn mà không cần khóa hoặc nếu chúng sử dụng khóa, thì chúng sẽ thực hiện theo cách đảm bảo rằng "lệnh khóa" toàn cầu không bị vi phạm. Chuỗi là cấu trúc dữ liệu bất biến mà phương thức của chúng không làm thay đổi bất cứ điều gì, vì vậy chuỗi có thể dễ dàng được thực hiện an toàn.
Eric Lippert

11

Không có luật chuyên chế.

Dưới đây là một số quy tắc để làm cho chuỗi mã an toàn trong .NET và tại sao đây không phải là các quy tắc tốt:

  1. Hàm và tất cả các hàm mà nó gọi phải là thuần túy (không có tác dụng phụ) và sử dụng các biến cục bộ. Mặc dù điều này sẽ làm cho chuỗi mã của bạn an toàn, nhưng cũng có rất ít điều thú vị bạn có thể làm với hạn chế này trong .NET.
  2. Mỗi chức năng hoạt động trên một đối tượng chung phải locktrên một điều chung. Tất cả các khóa phải được thực hiện theo cùng một thứ tự. Điều này sẽ làm cho chuỗi mã an toàn, nhưng nó sẽ cực kỳ chậm và bạn cũng có thể không sử dụng nhiều luồng.
  3. ...

Không có quy tắc nào làm cho luồng mã an toàn, điều duy nhất bạn có thể làm là đảm bảo rằng mã của bạn sẽ hoạt động bất kể nó được thực thi bao nhiêu lần, mỗi luồng có thể bị gián đoạn tại bất kỳ điểm nào, với mỗi luồng được đưa vào trạng thái / vị trí riêng của nó và điều này cho từng chức năng (tĩnh hoặc khác) đang truy cập các đối tượng phổ biến.


10
Khóa không cần phải chậm. Khóa rất nhanh; một khóa không thể kiểm tra được theo thứ tự cường độ từ mười đến một trăm nano giây . Khóa bị tranh chấp tất nhiên là chậm tùy ý; nếu bạn bị chậm lại vì bạn đang tranh chấp các khóa thì hãy tìm kiếm lại chương trình để loại bỏ sự tranh chấp .
Eric Lippert

2
Tôi đoán tôi đã thất bại trong việc làm cho số 2 đủ rõ ràng. Tôi đã cố gắng nói rằng có một cách để làm cho các luồng an toàn: đặt các khóa xung quanh mỗi lần truy cập của bất kỳ đối tượng phổ biến nào. Và giả sử rằng có nhiều luồng, nó sẽ tạo ra sự tranh chấp và các khóa sẽ làm chậm toàn bộ sự việc. Khi thêm khóa, người ta không thể thêm khóa một cách mù quáng, chúng phải được đặt ở những vị trí chiến lược rất tốt.
Earl Namless 24/03 '

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.