2010-02-11 21 views
7

Tuyên bố miễn trừ trách nhiệm: Bài đăng của tôi dường như luôn dài dòng. Nếu bạn biết câu trả lời cho câu hỏi tiêu đề, vui lòng chỉ trả lời câu hỏi mà không đọc thảo luận mở rộng của tôi bên dưới.Tại sao Interlocked.CompareExchange <T> chỉ hỗ trợ các loại tham chiếu?


Lớp System.Threading.Interlocked cung cấp một số phương pháp rất hữu ích để hỗ trợ cho việc viết mã thread-safe. Một trong những phương pháp phức tạp hơn là CompareExchange, có thể được sử dụng để tính toán tổng số đang chạy có thể được cập nhật từ nhiều luồng.

Kể từ khi việc sử dụng các CompareExchange là một chút khó khăn, tôi nghĩ rằng nó là một ý tưởng khá phổ biến-tinh thần để cung cấp một số phương pháp helper cho nó:

// code mangled so as not to require horizontal scrolling 
// (on my monitor, anyway) 
public static double Aggregate 
(ref double value, Func<double, double> aggregator) { 
    double initial, aggregated; 

    do { 
     initial = value; 
     aggregated = aggregator(initial); 
    } while (
     initial != Interlocked.CompareExchange(ref value, aggregated, initial) 
    ); 

    return aggregated; 
} 

public static double Increase(ref double value, double amount) { 
    return Aggregate(ref value, delegate(double d) { return d + amount; }); 
} 

public static double Decrease(ref double value, double amount) { 
    return Aggregate(ref value, delegate(double d) { return d - amount; }); 
} 

Bây giờ, có lẽ tôi chỉ có tội là generic-hạnh phúc (Tôi sẽ thừa nhận, điều này thường đúng); nhưng nó cảm thấy ngớ ngẩn với tôi để hạn chế chức năng được cung cấp bởi các phương pháp trên chỉ với giá trị double (hoặc, chính xác hơn, đối với tôi phải viết các phiên bản quá tải của các phương pháp trên cho mọi loại tôi muốn hỗ trợ). Tại sao tôi không thể làm điều này?

// the code mangling continues... 
public static T Aggregate<T> 
(ref T value, Func<T, T> aggregator) where T : IEquatable<T> { 
    T initial, aggregated; 

    do { 
     initial = value; 
     aggregated = aggregator(initial); 
    } while (
     !initial.Equals(
      Interlocked.CompareExchange<T>(ref value, aggregated, initial) 
     ) 
    ); 
} 

tôi không thể làm điều này vì Interlocked.CompareExchange<T> dường như có một hạn chế where T : class, và Tôi không hiểu tại sao. Ý tôi là, có lẽ đó là do đã quá tải cho CompareExchange chấp nhận Int32, Int64, Double, v.v ...; nhưng điều đó hầu như không có lý do chính đáng. Trong trường hợp của tôi, ví dụ, nó sẽ khá tiện dụng để có thể sử dụng phương pháp Aggregate<T> để thực hiện một loạt các tính toán nguyên tử.

Trả lời

11

Interlocked.CompareExchange được thực hiện với các hướng dẫn nguyên tử gốc do bộ xử lý cung cấp trực tiếp. Nó là vô nghĩa để có một cái gì đó như thế sử dụng một lock nội bộ (nó được thiết kế cho các kịch bản không có khóa).

Bộ xử lý cung cấp hướng dẫn trao đổi so sánh nguyên tử tự nhiên hỗ trợ thao tác nhỏ, "kích thước đăng ký" (ví dụ: lệnh trao đổi so sánh lớn nhất trên bộ xử lý Intel x64 là cmpxchg16b hoạt động trên giá trị 128 bit).

Loại giá trị tùy ý có thể có khả năng lớn hơn và so sánh trao đổi nó có thể không thực hiện được với một lệnh duy nhất. So sánh trao đổi một loại tham chiếu thật dễ dàng. Bất kể tổng kích thước của nó trong bộ nhớ, bạn sẽ so sánh và sao chép một con trỏ nhỏ có kích thước đã biết. Điều này cũng đúng đối với các loại nguyên thủy như Int32Double — tất cả chúng đều nhỏ.

+0

Tôi nghĩ rằng điều này tập trung vào tôi một khi tôi đọc điều này trong tài liệu MSDN ngay sau khi đăng câu hỏi (cho quá tải chấp nhận tham số 'Object'):" Các đối tượng được so sánh cho bình đẳng tham chiếu, thay vì 'Object.Equals' Kết quả là, hai trường hợp đóng hộp cùng loại giá trị (ví dụ, số nguyên 3) luôn xuất hiện là không bằng nhau và không có thao tác nào được thực hiện. Không sử dụng quá tải này với các kiểu giá trị. " Tất nhiên - nó sẽ không sử dụng phương thức 'Equals', mà là tự kiểm tra các tham chiếu. –

+0

Vì vậy, rõ ràng sự lựa chọn duy nhất của tôi thực ra là viết tất cả những phương thức quá tải đó (cho 'int',' long', vv) sau tất cả - nếu tôi thực sự muốn đi theo con đường đó. Đúng? –

+0

@Dan: Tôi không chắc chắn làm thế nào bạn sẽ làm điều đó cho một loại giá trị lớn hơn * nguyên tử * mà không sử dụng một 'khóa' (hoán đổi một loạt các giá trị số nguyên). Điểm trao đổi so sánh là nó là nguyên tử; nghĩa là, sẽ không có một thời điểm nào khi bạn có thể quan sát một phần của hoạt động được thực hiện và phần kia thì không. Đặt một loạt các trao đổi so sánh với nhau sẽ không giúp làm cho nguyên tử hoạt động tổng thể (mỗi phần là nguyên tử, nhưng toàn bộ hoạt động, không phải là). –

-1

Tôi nghi ngờ rằng Interlocked.CompareExchange<T> chỉ thực hiện chuyển đổi con trỏ nguyên tử dưới mui xe.

Cố gắng thực hiện điều đó với một loại giá trị có thể sẽ không cung cấp kết quả bạn mong đợi.

Bạn có thể, tất nhiên, loại giá trị hộp lên trong một object trước khi sử dụng chúng.

+1

Bật ra boxing các loại giá trị như 'đối tượng' sẽ ** không ** làm việc, thực sự. Xem bình luận đầu tiên của tôi cho câu trả lời của Mehrdad. –

1

Do quá tải đó được thiết kế để so sánh và trao đổi tham chiếu. Nó không thực hiện kiểm tra bình đẳng bằng phương thức Equals().Vì loại giá trị sẽ không bao giờ có tham chiếu bình đẳng với giá trị mà bạn so sánh với, nên tôi đoán là chúng đã hạn chế T thành lớp để tránh lạm dụng.

+0

Tôi đặt cược tên cuối cùng của bạn mang đến cho bạn nhiều niềm tin trong số các đồng nghiệp của bạn. –

+0

Đó là một +1 từ tôi, nhân tiện; Tôi đã không được snide. –

+0

LOL không sao, tôi đã không lấy nó như vậy. Câu hỏi rất hay nữa. – Josh

Các vấn đề liên quan