2009-08-17 37 views
16

Tôi đang cố khóa một đối tượng "đóng hộp" trong ứng dụng C#, điều này là không thể?C# luồng - Khóa đối tượng

class t 
    { 
     System.Object t_x = new object(); 

     public t(int p) 
     { 
      t_x = p; 

     } 

     public void w() 
     { 
      lock (t_x) 
      { 
       for (int i = 0; i < 4; i++) 
       { 

        { 
         t_x = ((int)t_x) + 1; 
         Console.WriteLine(t_x); 
         Thread.Sleep(1000); 
        } 
       } 
      } 
     } 
    } 

Trong lớp khác tôi có thể bắt đầu 2 chủ đề:

 Thread b1 = new Thread(new ThreadStart(t1.w)); 
     b1.Start(); 
     Thread b2 = new Thread(new ThreadStart(t1.w)); 
     b2.Start(); 

Tuy nhiên phần không bị khóa. Khi tôi khóa một đối tượng tùy ý (tức là một đối tượng được tạo và không được sửa đổi làm đối tượng a = new object()) nó khóa tốt. Hoạt động đấm bốc có bằng cách nào đó "làm suy yếu" đối tượng của tôi không ??

+2

Tại sao bạn sẽ phân công lại đối tượng khóa trong một khối khóa? Điều đó đánh bại mục đích - vì toàn bộ quan điểm đồng bộ hóa là để tránh các chủ đề khác nhau chà đạp lẫn nhau cho một tài nguyên "được chia sẻ". – Gishu

+0

Tôi nghĩ rằng câu trả lời được chấp nhận nó không phải là tốt –

+0

Dường như có một sự lặp lại định kỳ trong số các nỗ lực ít kinh nghiệm. Net luồng rằng các tuyên bố khóa thực sự "khóa" hoặc bảo vệ các đối tượng mà khóa được gọi là. Họ dường như bỏ lỡ một thực tế rằng đối tượng thực sự là một chìa khóa cho một khối mã bị khóa, phần quan trọng. –

Trả lời

16

Bạn cần phải tạo một đối tượng khóa riêng biệt. Vấn đề là bạn gán lại t_x bên trong vòng lặp. Giả sử thread b1 nằm bên trong vòng lặp trước khi b2 nhận được thông báo khóa, b2 sẽ được cho phép bên trong câu lệnh khóa bởi vì, vào thời điểm đó, t_x sẽ là một đối tượng mới không có khóa trên đó.

+0

Tôi nghĩ Màn hình là cách tốt hơn để làm điều đó –

+4

@Cedric - đọc câu trả lời của Lee cẩn thận hơn. Câu lệnh 'lock' giống hệt như sử dụng' Monitor.Enter' và 'Monitor.Exit'. Nó chỉ là một cú pháp neater cho cùng một mẫu. Vấn đề là không sử dụng 'lock' hoặc' Monitor' (không thực sự là một lựa chọn, vì chúng giống nhau). Vấn đề là luôn luôn vượt qua cùng một đối tượng để phục vụ như là khóa, bị hỏng nếu bạn sử dụng một loại giá trị vì nó được đóng gói khác nhau bao giờ bạn truy cập nó. –

23

Không, bạn không thể làm điều này - khối khóa là viết tắt sau:

try(Monitor.Enter(lockObject)) 
{ 
    //critical section 
} 
finally 
{ 
    Monitor.Exit(lockObject) 
} 

Các documentation for Monitor.Enter tiểu bang, "Sử dụng Monitor để khóa đối tượng (có nghĩa là, các loại tài liệu tham khảo), chứ không phải các loại giá trị Khi bạn chuyển biến kiểu giá trị thành Enter, nó sẽ được đóng thành một đối tượng. Nếu bạn chuyển cùng một biến thành Enter, nó sẽ được đóng thành một đối tượng riêng biệt và luồng không bị chặn "

+0

Đó là cách tốt để làm điều đó. Đó là trong kỳ thi đầu tiên 70-536 của chứng nhận microsoft. –

+7

Đây là câu trả lời đúng - không phải vì đoạn mã (câu lệnh 'khóa' là tốt) mà bởi vì nó đề cập đến sự khác biệt giữa giá trị và kiểu tham chiếu. Một loại giá trị được đóng hộp một lần nữa mỗi lần nó được chuyển đổi thành lớp cơ sở 'đối tượng' chung, vì vậy nó không thể hoạt động đáng tin cậy như là mục tiêu khóa. Vì vậy, điều này là tốt hơn so với câu trả lời được chấp nhận. –

2

Bạn phải sử dụng một đối tượng bổ sung cho khóa

object lockObj = new object(); 
public void foo() 
{ 
    lock(lockObj) 
    { 
    //do stuff here 
    } 
} 
2

Hộp gọi (t_x) gọi một số nguyên làm đối tượng tạm thời. Mỗi cuộc gọi để khóa (t_x) tạo ra một đối tượng mới và khóa là vô ích.

(Khóa hy vọng một đối tượng và tạo ra một đối tượng tạm thời MỚI từ số nguyên)

Chỉ cần tạo một đối tượng khóa riêng biệt như đã nói ở trên bởi Femaref.

0

Nếu bạn thực sự muốn (? Cần) để khóa trên đối tượng, bạn có thể sử dụng một loại wrapper:

public class IntWrapper 
{ 
    public int Value{get;set;} 
} 

Hoặc nếu bạn cần phải ở lại trừu tượng hơn:

public class ObjectWrapper 
{ 
    public Object Value { get;set; } 
} 
+0

Trong khi tôi sẽ không khuyên bạn nên làm theo cách này (chỉ cần sử dụng một 'đối tượng' để khóa trên), tôi không hiểu những downvotes, vì nó * là * chính xác. –

0

Nếu bạn muốn nhận ra khi dữ liệu được tải và nếu sử dụng cố gắng sử dụng nó trước đó, bạn có thể làm như sau:

Có cờ boolean như bạn đề cập, nhưng sử dụng một đối tượng riêng để khóa trước truy cập nó để ngăn chặn điều kiện chủng tộc chéo.

Khi người dùng cố gắng sử dụng dữ liệu, nếu nó không được tải (kiểm tra biến), bạn có thể thêm một trình xử lý sự kiện khác vào sự kiện RunWorkerCompleted của nhân viên, ngay lập tức sẽ làm những gì người dùng muốn khi dữ liệu được tải.

Ví dụ:

public class MyClass 
{ 
    private bool dataIsReady = false; 
    private object locker = new object(); 
    BackgroundWorker worker; 

    public void Begin() 
    { 
     worker = new BackgroundWorker(); 
     worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
    } 

    public void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     lock (locker) 
     { 
      dataIsReady = true; 
     } 
    } 

    public void UseTriesToUseData() 
    { 
     lock (locker) 
     { 
      if (dataIsReady) 
      { 
       DoStuff(); 
      } 
      else 
      { 
       this.worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(DoStuffCaller); 
      } 
     } 
    } 

    private void DoStuff() 
    { 
     // Do stuff with data. 
    } 

    private void DoStuffCaller(object sender, RunWorkerCompletedEventArgs e) 
    { 
     this.DoStuff(); 
    } 
} 
Các vấn đề liên quan