2009-02-27 36 views
17

Tôi đọc trong tài liệu MS chỉ định giá trị 64 bit trên máy tính Intel 32 bit không phải là hoạt động nguyên tử; đó là, hoạt động không phải là luồng an toàn. Điều này có nghĩa là nếu hai người cùng lúc gán một giá trị cho trường tĩnh Int64, thì giá trị cuối cùng của trường không thể dự đoán được.Dưới C# là sử dụng Int64 trên bộ xử lý 32 bit nguy hiểm

Ba phần câu hỏi:

  • Đây có phải là thực sự đúng?
  • Đây có phải là điều tôi sẽ lo lắng trong thế giới thực không?
  • Nếu đơn đăng ký của tôi là đa luồng, tôi có thực sự cần bao quanh tất cả các bài tập Int64 của mình với mã khóa không?
+3

Đối với các hoạt động nguyên tử trên Int64, bạn có thể sử dụng lớp InterLocked (http://msdn.microsoft.com/en-us/library/system.threading.interlocked.add.aspx). –

Trả lời

18

Đây không phải về mọi biến mà bạn gặp phải. Nếu một số biến được sử dụng làm trạng thái được chia sẻ hoặc một thứ gì đó (bao gồm nhưng không giới hạn ở một số trường là static), bạn nên giải quyết vấn đề này. Nó hoàn toàn không có vấn đề đối với các biến cục bộ không được treo như là kết quả của việc đóng lại trong một đóng hoặc một phép biến đổi vòng lặp và được sử dụng bởi một hàm duy nhất (và do đó, một luồng đơn) tại một thời điểm.

+0

Điều này là đúng, tuy nhiên có thể không rõ lý do. Int64 được kế thừa từ Hệ thống.ValueType, có nghĩa là giá trị được lưu trữ trên ngăn xếp. Vì mỗi luồng đều có ngăn xếp cuộc gọi riêng, mỗi luồng có giá trị riêng, ngay cả khi gọi cùng một hàm. – codekaizen

+0

tưởng tượng lớp X {int n; }. Nó là tham chiếu hay kiểu giá trị? Nó sẽ được lưu trữ trong heap hoặc trên stack? –

+0

DK, tôi không nghĩ đây là một câu hỏi có liên quan nhưng các lớp là các kiểu tham chiếu và được lưu trữ trong một đống. Nếu bạn giữ một tham chiếu đến một lớp chỉ trong một chủ đề duy nhất, bạn vẫn sẽ không cần phải lo lắng về vấn đề khóa. –

7

MSDN:

Gán một thể hiện của loại này là không đề an toàn trên tất cả các phần cứng nền tảng vì nhị phân đại diện cấp sơ thẩm có thể quá lớn để gán trong một hoạt động nguyên tử duy nhất.

Nhưng cũng:

Như với bất kỳ loại nào khác, đọc và bằng văn bản cho một biến chia sẻ rằng chứa một thể hiện của loại hình này phải được bảo vệ bởi một khóa để đảm bảo chủ đề an toàn.

+2

Đúng, từ khóa là ** biến chia sẻ **. –

1

Trên nền tảng x86 32 bit, bộ nhớ có kích thước nguyên tử lớn nhất là 32 bit.

Điều này có nghĩa là nếu có gì đó ghi hoặc đọc từ biến có kích thước 64 bit thì có thể đọc/ghi đó để được làm trống trước trong khi thực thi.

  • Ví dụ: bạn bắt đầu gán giá trị cho biến 64 bit.
  • Sau 32 bit đầu tiên được viết hệ điều hành quyết định rằng một quá trình khác sẽ nhận được thời gian CPU.
  • Quy trình tiếp theo cố gắng đọc biến bạn đang ở giữa chuyển nhượng.

Đó chỉ là một điều kiện chủng tộc có thể có với phân bổ 64 bit trên nền tảng 32 bit.

Tuy nhiên, ngay cả với biến 32 bit, có thể có các điều kiện chủng tộc với việc đọc và viết cho bất kỳ biến chia sẻ nào cần được đồng bộ hóa theo một cách nào đó để giải quyết các điều kiện chủng tộc này.

+0

"Trên nền tảng x86 32 bit, mảnh bộ nhớ có kích thước nguyên tử lớn nhất là 32 bit". - Sai rồi. Bạn có thể viết 8 byte nguyên tử qua fstp/mmx/sse. –

0

Điều này có thực sự đúng không?Có, khi nó quay ra. Nếu thanh ghi của bạn chỉ có 32 bit trong đó, và bạn cần lưu trữ một giá trị 64 bit đến một số vị trí bộ nhớ, nó sẽ lấy hai hoạt động tải và hai hoạt động lưu trữ. Nếu quá trình của bạn bị gián đoạn bởi quá trình khác giữa hai tải/cửa hàng này, quá trình khác có thể làm hỏng một nửa dữ liệu của bạn! Lạ nhưng có thật. Điều này đã được một vấn đề trên mỗi bộ xử lý từng được xây dựng - nếu datatype của bạn dài hơn đăng ký của bạn, bạn sẽ có vấn đề đồng thời.

Đây có phải là điều tôi sẽ lo lắng trong thế giới thực không? Có và không. Vì hầu như tất cả các chương trình hiện đại đều được cung cấp không gian địa chỉ riêng của nó, bạn sẽ chỉ cần lo lắng về điều này nếu bạn đang làm lập trình đa luồng.

Nếu ứng dụng của tôi là đa luồng, tôi có thực sự cần bao quanh tất cả các bài tập Int64 của tôi bằng mã khóa không? Đáng buồn thay, có nếu bạn muốn nhận được kỹ thuật. Thông thường, việc sử dụng Mutex hoặc Semaphore xung quanh các khối mã lớn hơn thường dễ hơn là khóa từng câu lệnh thiết lập riêng lẻ trên các biến có thể truy cập trên toàn cầu.

2

Nếu bạn có biến chia sẻ (ví dụ như trường tĩnh của một lớp hoặc trường của đối tượng được chia sẻ) và trường hoặc đối tượng đó sẽ được sử dụng cross-thread, thì, vâng, bạn cần đảm bảo rằng quyền truy cập vào biến đó được bảo vệ thông qua hoạt động nguyên tử. Bộ vi xử lý x86 có nội tại để đảm bảo điều này xảy ra và cơ sở này được hiển thị thông qua các phương thức lớp System.Threading.Interlocked.

Ví dụ:

class Program 
{ 
    public static Int64 UnsafeSharedData; 
    public static Int64 SafeSharedData; 

    static void Main(string[] args) 
    { 
     Action<Int32> unsafeAdd = i => { UnsafeSharedData += i; }; 
     Action<Int32> unsafeSubtract = i => { UnsafeSharedData -= i; }; 
     Action<Int32> safeAdd = i => Interlocked.Add(ref SafeSharedData, i); 
     Action<Int32> safeSubtract = i => Interlocked.Add(ref SafeSharedData, -i); 

     WaitHandle[] waitHandles = new[] { new ManualResetEvent(false), 
              new ManualResetEvent(false), 
              new ManualResetEvent(false), 
              new ManualResetEvent(false)}; 

     Action<Action<Int32>, Object> compute = (a, e) => 
              { 
               for (Int32 i = 1; i <= 1000000; i++) 
               { 
                a(i); 
                Thread.Sleep(0); 
               } 

               ((ManualResetEvent) e).Set(); 
              }; 

     ThreadPool.QueueUserWorkItem(o => compute(unsafeAdd, o), waitHandles[0]); 
     ThreadPool.QueueUserWorkItem(o => compute(unsafeSubtract, o), waitHandles[1]); 
     ThreadPool.QueueUserWorkItem(o => compute(safeAdd, o), waitHandles[2]); 
     ThreadPool.QueueUserWorkItem(o => compute(safeSubtract, o), waitHandles[3]); 

     WaitHandle.WaitAll(waitHandles); 
     Debug.WriteLine("Unsafe: " + UnsafeSharedData); 
     Debug.WriteLine("Safe: " + SafeSharedData); 
    } 
} 

Kết quả:

không an toàn: -24050275641 Safe: 0

Trên một mặt lưu ý thú vị, tôi chạy này ở chế độ x64 trên Vista 64. Điều này cho thấy các trường 64 bit được xử lý như các trường 32 bit theo thời gian chạy, nghĩa là các hoạt động 64 bit không phải là nguyên tử. Bất cứ ai biết nếu đây là một vấn đề CLR hoặc một vấn đề x64?

+0

Như Jon Skeet và Ben S chỉ ra, điều kiện chủng tộc có thể xảy ra giữa lần đọc và viết, vì vậy bạn không thể suy ra rằng các bài viết không phải là nguyên tử. –

+0

Tôi không hiểu ... lập luận đó đi theo một trong hai cách. Theo như tôi có thể nói, dữ liệu vẫn sai. Nếu bạn chạy ví dụ, rõ ràng là dữ liệu kết thúc sai do các hoạt động phi nguyên tử. – codekaizen

+0

Sự cố không phải là CLR hoặc x64. Đó là với mã của bạn. Những gì bạn đang cố gắng làm là đọc nguyên tử + cộng/trừ + viết. Trong khi đó trong x64 bạn được đảm bảo đọc/ghi nguyên tử của int64. Một lần nữa, điều này khác với đọc nguyên tử + thêm + ghi. –

12

Ngay cả khi viết nguyên tử, rất có thể bạn vẫn sẽ cần phải lấy ra một khóa bất cứ khi nào bạn truy cập biến. Nếu bạn không làm điều đó, ít nhất bạn phải tạo biến số volatile để đảm bảo rằng tất cả các chuỗi đã thấy giá trị mới vào lần tiếp theo họ đọc biến đó (hầu như luôn là thứ bạn muốn). Điều đó cho phép bạn làm các bộ nguyên tử, dễ bay hơi - nhưng ngay sau khi bạn muốn làm bất cứ điều gì thú vị hơn, chẳng hạn như thêm 5 vào nó, bạn sẽ trở lại khóa.

Khóa lập trình miễn phí rất, rất khó để có được quyền. Bạn cần biết chính xác những gì bạn đang làm và giữ sự phức tạp thành một đoạn mã nhỏ nhất có thể. Cá nhân, tôi hiếm khi cố gắng thử nó ngoài các mẫu rất nổi tiếng như sử dụng bộ khởi tạo tĩnh để khởi tạo một bộ sưu tập và sau đó đọc từ bộ sưu tập mà không khóa.

Sử dụng lớp học Interlocked có thể giúp trong một số trường hợp, nhưng hầu như luôn dễ dàng hơn khi chỉ lấy ra một khóa. Các khóa không được kiểm soát là "khá rẻ" (thừa nhận chúng đắt tiền hơn, nhưng mọi thứ cũng vậy) - không gây rối với mã khóa cho đến khi bạn có bằng chứng rõ ràng rằng nó thực sự tạo ra sự khác biệt đáng kể.

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