2013-08-23 39 views
5

Tôi có thể thiếu một số thứ cơ bản ở đây, nhưng vẫn sẽ đánh giá cao sự giúp đỡ của các bạn trong việc hiểu điều này. Vì vậy, tôi có các chương trình đa luồng đơn giản sau đây tôi đã viết:Đơn giản C# đồng thời/đa luồng

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

namespace ConsoleApplication2 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      //   List<int> outcome = new List<int>(); 
      Test t = new Test(); 

       Thread thread1 = new Thread(new ThreadStart(t.call1)); 
       Thread thread2 = new Thread(new ThreadStart(t.call2)); 
       thread1.Start(); 
       thread2.Start(); 
       Thread.Sleep(3000); //Give enough time for threads to end 
       Console.Write("{0},", t.mSum); 
       t.mSum = 0; 
     } 
    } 

    class Test 
    { 
     public int mSum = 0; 
     public void call1() 
     { 
      //lock (this) 
      //{ 

      for (int i = 0; i < 100; i++) 
      { 
       Console.WriteLine("Hello Thread 1, mSum value: {0}", mSum); 
       mSum = mSum + 1; 
       Console.WriteLine("Goodbye Thread 1, mSum value: {0}", mSum); 
      } 
      //} 
      // Console.WriteLine(mSum); 
     } 
     public void call2() 
     { 
      for (int i = 0; i < 100 ; i++) 
      { 
       Console.WriteLine("Hello Thread 2, mSum value: {0}",mSum); 
       mSum = mSum + 1; 
       Console.WriteLine("Goodbye Thread 2, mSum value: {0}",mSum); 
      } 
     } 
    } 
}  

Vì vậy, tôi mong chờ kết quả này là nondetermenistic vì bối cảnh chuyển đổi có thể xảy ra bất cứ lúc nào phải không? Nhưng khi tôi chạy chương trình, tôi nhận được đầu ra sau (chỉ là một phần của đầu ra, bị thay đổi do kỹ năng stackoverflow.com câu hỏi gửi bài nghèo của tôi):

 
Hello Thread 1, mSum value: 62 Goodbye Thread 1, mSum value: 63 
Hello Thread 1, mSum value: 63 Goodbye Thread 1, mSum value: 64 
Hello Thread 2, mSum value: 59 Goodbye Thread 2, mSum value: 65 
Hello Thread 2, mSum value: 65 Goodbye Thread 2, mSum value: 66 

Vì vậy, giả sử tôi viết điều này đúng, và mSum thực sự được chia sẻ giữa các chủ đề (trông giống như nó ...) - làm thế nào tôi có thể giải thích dòng không. 3? Chủ đề 2 đọc 59, thêm 1 và sau đó chúng tôi nhận được 65!

Tôi có khám phá ra một loại toán học mới không? :)

+0

Và đây là thứ tự đúng mà không cần tái cơ cấu? Mã cho thấy rằng phải có một ngắt dòng giữa dòng "Hello" và dòng "Tạm biệt" và nếu bạn xóa nó là tốt, tôi chỉ cố gắng để có được một cảm giác để đảm bảo rằng nó đã đi "Xin chào 1, tạm biệt 1 ... xin chào 2, tạm biệt 2 "Nếu bạn biết tôi nói gì" –

+0

Có thứ tự đúng: từ trái qua phải, sau đó lên đến dưới (62-> 63,63-> 64,59-> 65, 65-> 66) Tôi cố gắng thêm một hình ảnh vào đây nhưng không thể – Tal

Trả lời

9

Bạn không khóa biến được chia sẻ mSummSum = mSum + 1 không phải là hoạt động nguyên tử. Và nó sẽ được rõ ràng rằng in ấn để điều khiển, incrementing một biến và sau đó in ấn để bàn giao tiếp một lần nữa không phải là nguyên tử tất cả hơn :) Có rất nhiều cách có thể các chủ đề có thể xen kẽ. Ví dụ:

1) mSum = 0 [thread1 đang làm việc]

2) mSum = 1 [thread1 đang làm việc]

3) mSum = 2 [thread2 đang làm việc]

4) ...

5) mSum = 59 [thread2 đang làm việc] và nó được bị giành trước sau "Xin chào ..."

6) mSum = 60 [Thre AD1 đang làm việc]

7) mSum = 61 [thread1 đang làm việc]

8) ...

9) mSum = 64 [thread2 đang làm việc] đánh thức ngay trước khi dòng incrementation thread2 vẫn tiếp tục, và tính toán 65

trong 5)Thread2 thể đã bị giành trước thậm chí sau khi đọc mSum từ bộ nhớ trong mSum = mSum + 1 nhưng trước khi tính toán mSum + 1.

3

Vì bạn đang sử dụng nhiều luồng, giá trị có thể thay đổi giữa cuộc gọi đầu tiên và thứ hai Console.WriteLine.

Nếu bạn muốn đảm bảo rằng bạn đang báo cáo giá trị chính xác mà phần bổ sung sẽ sử dụng, bạn sẽ phải sử dụng khóa.

0

Như bạn chỉ ra, mSum đang được chia sẻ giữa nhiều chủ đề, độ phân giải sẽ hoặc là một khóa hoặc biến mSum thành một volatile số nguyên:

http://msdn.microsoft.com/en-us/library/x13ttww7.aspx

Vì vậy, bạn có thể thay đổi điều này:

public int mSum = 0; 

Đến:

public volatile int mSum = 0; 

Nhưng như những người khác đã chỉ ra, trong các tình huống phức tạp hơn, bạn có thể muốn sử dụng khóa.

Ngoài ra, có không cần phải quấn cuộc gọi của bạn trong một trường hợp mới của ThreadStart, vì vậy thay vì điều này:

new Thread(new ThreadStart(t.call1)); 

Bạn có thể làm điều này:

new Thread(t.call1); 
+0

Xin chào, tôi biết đây không phải là chủ đề an toàn và tôi biết tôi nên sử dụng khóa để * giải quyết * điều này nhưng tôi chỉ đang cố gắng Hiểu được cách .NET hoạt động nội bộ thông qua mã này.Điều đó đang được nói, mSum được chia sẻ nhưng tôi cố gắng hiểu được logic – Tal

+0

Nó không thực sự được chia sẻ, nó chỉ được truy cập từ nhiều luồng. Như đã nói, Sean, giá trị của mSum có thể thay đổi giữa WriteLine và bổ sung –

0

Các mSum được thay đổi từ 2 luồng A và B cùng lúc giữa WriteLine và WriteLine mSum thứ hai có thể thay đổi. mSum không bị khóa hoặc biến động nên bạn không có bất kỳ rào cản nào trong bộ nhớ, do đó bạn có thể trở thành một kết quả rất lạ nhưng nó phụ thuộc vào loại CPU và tiền mặt của bạn.

Chỉ cần đặt từ biến động trước mSum có nghĩa là mSum sẽ không được đổi thành tiền mặt trong CPU. Chạy lại ứng dụng và xem giao diện điều khiển !?

+0

Có vẻ như không có vấn đề bao nhiêu lần tôi chạy chương trình, kết quả cuối cùng cho mSum luôn luôn là 200 ngay cả wi không có ổ khóa. Tôi đã làm rất nhiều lần. – Tal

+1

@Tal Chỉ vì điều kiện chủng tộc tồn tại không có nghĩa là chúng xảy ra mỗi lần. Ý tưởng trong khi đó là họ có thể hoặc có thể không xảy ra. Các điều kiện chủng tộc khác nhau cũng có xác suất khác nhau xảy ra, có thể bị ảnh hưởng bởi các hành vi khác. Ví dụ, bạn có thể chạy cùng một chương trình trên một máy tính khác với nhiều lõi/ít hơn, hoặc nhiều hơn/ít sức mạnh tổng quát hơn, và có được một kết quả khác. Nếu chương trình của bạn bị hỏng .01% thời gian có thể chấp nhận được với bạn không? Có lẽ nó là, trong trường hợp này. Bạn có quan tâm nếu ngân hàng của bạn bỏ lỡ khoản tiền gửi séc 0,01% thời gian? – Servy

+0

@Tal importent để biết rằng Bạn không thể đọc cùng một bit bộ nhớ hai lần tại cùng một thời điểm. Các hạt và ruột của nó khá phức tạp mặc dù, như một CPU đa lõi hiện đại duy trì tất cả các loại bộ nhớ cache về những thứ này. –