2009-12-09 31 views
5

Chúng tôi đã có một cuộc thảo luận nhỏ trong văn phòng và không có câu trả lời nào được ghi lại:Phương thức SetValue/GetValue của System.Array có an toàn không?

Có phải an toàn là System.Array.SetValue chủ đề an toàn không?

using System; 
using System.Text; 
using System.Threading; 

namespace MyApp 
{ 
    class Program 
    { 
     private static readonly object[] arr = new object[3]; 

     static void Main(string[] args) 
     { 
      string value1 = "hello"; 
      int value2 = 123; 
      StringBuilder value3 = new StringBuilder(); 
      value3.Append("this"); 
      value3.Append(" is "); 
      value3.Append("from the StringBuilder"); 

      var states = new object[] 
          { 
           new object[] {0, value1}, 
           new object[] {1, value2}, 
           new object[] {2, value3} 
          }; 

      ThreadPool.QueueUserWorkItem(MySetValue, states[0]); 
      ThreadPool.QueueUserWorkItem(MySetValue, states[1]); 
      ThreadPool.QueueUserWorkItem(MySetValue, states[2]); 
      Thread.Sleep(0); 

      Console.WriteLine("press enter to continue"); 
      Console.ReadLine(); 

      // print the result 
      Console.WriteLine("result:"); 
      for (int i = 0; i < arr.Length; i++) 
      { 
       Console.WriteLine("arr[{0}] = {1}", i, arr[i]); 
      } 

      // quit 
      Console.WriteLine("press enter to quit"); 
      Console.ReadLine(); 

     } 

     // callback 
     private static void MySetValue(object state) 
     { 
      var args = (object[]) state; 
      var index = (int)args[0]; 
      var value = args[1]; 
      arr[index] = value; // THREAD-SAFE ?? 
     } 
    } 
} 

Như bạn có thể thấy, mỗi chuỗi đều đặt mục khác nhau, duy nhất trong mảng tĩnh. Tôi nhìn sâu vào mã bằng cách sử dụng phản xạ (và nhìn vào mscorlib.pdb). Cuối cùng, có một cuộc gọi đến:

[MethodImplAttribute(MethodImplOptions.InternalCall)] 
private unsafe extern static void InternalSetValue(void * target, Object value); 

Tài liệu nào không được ghi lại. Hãy xem tài liệu MSDN của System.Array nói chung, và để SetValue(object, int) đặc biệt .. Không có gì về an toàn thread (hoặc có thể tôi thiếu một cái gì đó).

Như nó thể hiện qua câu trả lời của Jon Skeet đến một similar question:

Tôi tin rằng nếu mỗi thread chỉ công trình trên một phần riêng biệt của mảng, tất cả sẽ tốt

tôi đang cố gắng nhận được câu trả lời rõ ràng cho GetValue(int)SetValue(object, int) về vấn đề này. Có ai đó có liên kết tài liệu và/hoặc hiểu rõ hơn về InternalSetValue không?

+0

Đọc tốt về những gì "an toàn chủ đề" thực sự có nghĩa là bởi Eric Lippert: http://blogs.msdn.com/ericlippert/archive/2009/10/19/what-is-this-thing-you-call -read-safe.aspx –

+0

Trong ví dụ của bạn, phương thức SetValue sẽ không được gọi, thay vào đó một mã opel Stelem sẽ được trình biên dịch phát ra, và không phải là một mã opcode gọi. –

Trả lời

3

MSDN: Array class

tĩnh công cộng (chung trong Visual Basic) thành viên của loại này là chủ đề an toàn. Bất kỳ thành viên nào không phải là được đảm bảo là chuỗi an toàn.

Triển khai này không cung cấp trình bao bọc đồng bộ (ren an toàn) cho một mảng; tuy nhiên, các lớp .NET Framework dựa trên Array cung cấp phiên bản đồng bộ của riêng bộ sưu tập bằng cách sử dụng thuộc tính SyncRoot .

Đó không phải là chủ đề an toàn!

Edit:

Một số thông tin thêm, phương pháp SetValue trên lớp Array không được gọi trong trường hợp bình thường, nó chỉ gọi khi mảng được sử dụng thông qua giao diện IList.

đoạn mã sau:

int[] arr = ... 
arr[i] = value; 

sẽ không tạo ra một cuộc gọi đến SetValue(), thay vì một opcode OpCodes.Stelem sẽ được tạo ra để thay thế.

Vì vậy, nó không thích hợp nếu phương pháp SetValue là chuỗi an toàn hay không trừ khi mảng được truy cập bằng cách sử dụng tham chiếu IList.

+0

Tôi thực sự không biết làm thế nào tôi bỏ lỡ điều đó. Cảm ơn! –

+1

Vì vậy, nó không phải là thread-an toàn nói chung, nhưng làm thế nào về các kịch bản cụ thể với mỗi thread truy cập chỉ là một phần riêng biệt của mảng? –

+0

@divo, nó không được đảm bảo là luồng an toàn, có nghĩa là ngay cả khi triển khai hiện tại là chi tiết triển khai. –

0

Trong ví dụ của bạn trong đó mỗi chủ đề đặt một mục khác trong mảng, nó sẽ là chuỗi an toàn. Xem nó như một tủ lạnh, có ba lon bia trong đó, ba khác nhau đi vào tủ lạnh và chọn lon bia của mình, tất cả sẽ đi tốt, họ sẽ uống nó một nửa, đặt nó trở lại và trở lại cho nó sau này .

Tuy nhiên, bạn không thể chia sẻ 1 lon bia với ba người, không phải khi lập trình hoặc trong đời thực.

Vì vậy, yeah:

nếu mỗi thread chỉ hoạt động trên một phần riêng biệt của mảng, tất cả sẽ tốt

PS: Tôi không có nguồn để xác minh điều này mặc dù, nhưng xảy ra với sử dụng chủ đề tất cả các thời gian và tôi không bao giờ có một vấn đề đói (?) khi mỗi thread đọc 'n viết đồng thời trong một mảng. Tôi DO bị các vấn đề khi chia sẻ 'cùng một lon bia' vì vậy tôi tin rằng câu trả lời của tôi là chính xác, nhưng tôi muốn thấy ai đó xác minh điều này.

+0

Làm cách nào để bạn biết? Có lẽ lớp 'System.Array' tạo bộ nhớ cache của riêng nó trên mỗi lời gọi setter, và quá trình này không phải là thread-safe? –

0

Trong ví dụ của bạn, các cuộc gọi tới InternalSetValue(void *, object) đang được thực hiện cho ba vị trí bộ nhớ khác nhau. Vì vậy, nó nên được thread-an toàn. Việc ghi vào các vị trí đó sẽ không bị chảy máu vào các vị trí khác, ngay cả khi chúng là thành viên của cùng một mảng.

+0

Thật vậy, ví dụ mã gọi 'InternalSetValue' ba lần với các vị trí bộ nhớ khác nhau của cây, nhưng đó là điều tôi có thể nói. Tôi không biết thực hiện 'InternalSetValue' là gì, và do đó tôi không thể chắc chắn về sự an toàn của luồng của nó. –

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