2012-06-12 15 views
6

Tôi đã đọc qua bài đăng tips and tricks và tôi nghĩ mình sẽ thử một số công cụ C# mà tôi chưa từng làm trước đây. Do đó, mã sau đây không phục vụ mục đích thực sự, nhưng chỉ là một 'chức năng kiểm tra' để xem điều gì xảy ra.C# ThreadStatic + các thành viên dễ bay hơi không hoạt động như mong đợi

Dù sao, tôi có hai lĩnh vực private static:

private static volatile string staticVolatileTestString = ""; 
[ThreadStatic] 
private static int threadInt = 0; 

Như bạn thấy, tôi đang thử nghiệm ThreadStaticAttributedễ bay hơi từ khóa.

Dù sao, tôi có một phương pháp thử nghiệm mà trông như thế này:

private static string TestThreadStatic() { 
    // Firstly I'm creating 10 threads (DEFAULT_TEST_SIZE is 10) and starting them all with an anonymous method 
    List<Thread> startedThreads = new List<Thread>(); 
    for (int i = 0; i < DEFAULT_TEST_SIZE; ++i) { 
     Thread t = new Thread(delegate(object o) { 
      // The anon method sets a newValue for threadInt and prints the new value to the volatile test string, then waits between 1 and 10 seconds, then prints the value for threadInt to the volatile test string again to confirm that no other thread has changed it 
      int newVal = randomNumberGenerator.Next(10, 100); 
      staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " setting threadInt to " + newVal; 
      threadInt = newVal; 
      Thread.Sleep(randomNumberGenerator.Next(1000, 10000)); 
      staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " finished: " + threadInt; 
     }); 
     t.Start(i); 
     startedThreads.Add(t); 
    } 

    foreach (Thread th in startedThreads) th.Join(); 

    return staticVolatileTestString; 
} 

gì tôi mong đợi để xem trở về từ chức năng này là kết quả như thế này:

thread 0 setting threadInt to 88 
thread 1 setting threadInt to 97 
thread 2 setting threadInt to 11 
thread 3 setting threadInt to 84 
thread 4 setting threadInt to 67 
thread 5 setting threadInt to 46 
thread 6 setting threadInt to 94 
thread 7 setting threadInt to 60 
thread 8 setting threadInt to 11 
thread 9 setting threadInt to 81 
thread 5 finished: 46 
thread 2 finished: 11 
thread 4 finished: 67 
thread 3 finished: 84 
thread 9 finished: 81 
thread 6 finished: 94 
thread 7 finished: 60 
thread 1 finished: 97 
thread 8 finished: 11 
thread 0 finished: 88 

Tuy nhiên, những gì Tôi nhận được thông báo này là:

thread 0 setting threadInt to 88 
thread 4 setting threadInt to 67 
thread 6 setting threadInt to 94 
thread 7 setting threadInt to 60 
thread 8 setting threadInt to 11 
thread 9 setting threadInt to 81 
thread 5 finished: 46 
thread 2 finished: 11 
thread 4 finished: 67 
thread 3 finished: 84 
thread 9 finished: 81 
thread 6 finished: 94 
thread 7 finished: 60 
thread 1 finished: 97 
thread 8 finished: 11 
thread 0 finished: 88 

Kết quả 'thứ hai' như mong đợi tại trường ThreadStatic đang hoạt động như tôi nghĩ), nhưng có vẻ như một vài đầu ra ban đầu đã bị 'bỏ qua' từ nửa đầu tiên.

Ngoài ra, các chuỗi trong nửa đầu tiên bị lỗi, nhưng tôi hiểu rằng một chuỗi không chạy ngay khi bạn gọi Start(); nhưng thay vào đó các điều khiển hệ điều hành bên trong sẽ bắt đầu các chủ đề khi nó phù hợp. EDIT: Không có họ không, trên thực tế, tôi chỉ nghĩ rằng họ là bởi vì bộ não của tôi nhớ những con số liên tiếp


Vì vậy, câu hỏi của tôi là: Điều gì đang xảy ra sai gây ra cho tôi để mất một vài dòng trong đầu tiên 'nửa' của đầu ra? Ví dụ: chuỗi chủ đề 'thread 3' ở đâu là 84 '?

+0

Tôi chạy mã của bạn một vài lần và luôn nhận được kết quả mong đợi ... –

+0

Tôi cược rằng một nơi nào đó xuống dòng, cuộc gọi đến + = trên chuỗi là nhận được giá trị trước khi chuỗi trước đó đã thiết lập nó và chủ đề mới sẽ ghi đè nó bằng giá trị mới (do đó luồng 1 đã bị bỏ qua đầu ra). – Charleh

+0

@DannyChen Tôi đang sử dụng lõi tứ và đó không phải là mã duy nhất chạy trong ứng dụng; không biết nếu một trong số đó sẽ có ý nghĩa. Tôi có thể tải lên toàn bộ mã ở đâu đó (chỉ có hai tệp .cs) nếu bạn muốn? – Xenoprimate

Trả lời

7

Các chuỗi đang thực thi cùng một lúc. Có gì khái niệm xảy ra là thế này:

staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " setting threadInt to " + newVal; 
  1. Chủ đề 1 lần đọc staticVolatileTestString
  2. Chủ đề 2 lần đọc staticVolatileTestString
  3. Chủ đề 3 lần đọc staticVolatileTestString
  4. Chủ đề 1 gắn thêm những thứ và viết staticVolatileTestString lại
  5. Chủ đề 2 nối thêm nội dung và viết staticVolatileTestString quay lại
  6. Chủ đề 3 nối thêm nội dung và viết staticVolatileTestString về phía sau

Điều đó khiến cho các dòng của bạn bị mất. Dễ bay hơi không giúp được gì ở đây; toàn bộ quá trình nối chuỗi không phải là nguyên tử.Bạn cần phải sử dụng một khóa xung quanh những hoạt động:

private static object sync = new object(); 

lock (sync) { 
    staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " setting threadInt to " + newVal; 
} 
+0

tốt hơn giải thích: D haha ​​+1 – Charleh

2

MSDN mô tả những gì từ khóa volatile không here:

Các từ khóa dễ bay hơi chỉ ra rằng một lĩnh vực có thể được sửa đổi bởi nhiều luồng đồng thời thực hiện. Các trường được khai báo dễ bay hơi không phải tuân theo tối ưu hóa trình biên dịch giả sử truy cập theo một chuỗi duy nhất. Điều này đảm bảo rằng giá trị cập nhật nhất là có mặt tại hiện trường bất kỳ lúc nào.

Điều này có nghĩa, trong ví dụ của bạn, những gì xảy ra là về này (điều này có thể thay đổi bất cứ lúc nào; phụ thuộc vào lịch trình):

  • chủ đề 0 được đọc chuỗi ra khỏi staticVolatileTestString
  • chủ đề 0 được phụ thêm 'chủ đề 0 thiết lập threadInt 88'
  • chủ đề 0 được viết chuỗi trở lại vào staticVolatileTestString

này cũng giống như dự kiến ​​cho đến nay, nhưng sau đó:

  • chủ đề 1-4 đang đọc chuỗi ra khỏi staticVolatileTestString
  • thread 1 được phụ thêm 'thread 1 khung cảnh threadInt đến 97'
  • chủ đề 2 đang thêm 'thread 2 threadInt to 11'
  • chủ đề 2 đang viết chuỗi trở lại thành staticVolatileTestString
  • ... chủ đề 1, 2, 3 đang đọc và chắp thêm và viết
  • chủ đề 4 là viết chuỗi trở lại vào staticVolatileTestString
  • và vân vân ...

xem những gì xảy ra ở đây? thread 4 đọc chuỗi 'thread 0 thiết lập threadInt đến 88', gắn thêm 'thread 4 ...' của nó và viết nó trở lại, ghi đè lên tất cả mọi thứ thread 1, 2 và 3 đã viết vào chuỗi rồi.

+0

Vì vậy, bạn có nói rằng các chủ đề đang đọc giá trị đồng thời và sau đó viết như là một hoạt động riêng biệt trên mỗi khác? Nếu đúng như vậy, tôi cần một loại khóa trên sân (đó là điều tôi nghĩ là dễ bay hơi, nhưng tôi đoán nó thực sự chỉ đảm bảo rằng không có bộ nhớ đệm, đúng không?) EDIT: Câu trả lời của Lucero xác nhận điều này – Xenoprimate

+0

@Motig: Khóa ở đó khi đọc chuỗi, hoặc viết lại, nhưng không ở giữa. Hãy nhớ rằng: chuỗi không thay đổi được, do đó một thể hiện chuỗi mới được tạo và tham chiếu đến chuỗi đó được viết lại thành biến. –

+2

@Motig: từ khóa dễ bay hơi hoạt động tốt với các kiểu dữ liệu nguyên thủy có thể thay đổi được (bool, int, double, etc.). Tuy nhiên, với chuỗi, bạn muốn đặt khóa chuyên dụng, đọc chuỗi, sửa đổi và ghi lại, sau đó nhả khóa. –