2011-11-27 34 views
6

Tôi có một NullReferenceException cực kỳ lạ được ném khi đọc một giá trị từ một trường công trên một đối tượng mà tôi biết tồn tại. Dòng chảy cơ bản là thế này:Chủ đề lạ NullReferenceException khi đọc giá trị tồn tại?

Edit: tôi nhận ra tôi quên đề cập đến một cái gì đó quan trọng, điều này không xảy ra mỗi khi tôi cố gắng đọc giá trị Tag, nhưng chỉ somtimes, đủ để tôi có thể tái tạo nó mỗi thời gian bằng cách chỉ chạy mã, nhưng không phải ngay lập tức khi mã chạy

  • server nhận được một thông điệp (chủ đề Worker)
  • các kết nối đó gửi get nhắn được thiết lập như là lĩnh vực Tag trên đối tượng nhắn (Worker thread)
  • Thông điệp được đưa vào một "ReceivedMessages" hàng đợi (một đối tượng Queue bình thường được bảo vệ bởi ổ khóa để truy cập tuần tự) (Worker thread)
  • Thông điệp được đọc (Chủ đề chính)
  • Tôi cố gắng đọc trường Tag của thông báo để nhận kết nối, điều này đôi khi trả về null và ném ngoại lệ, nhưng khi ngoại lệ bị ném và tôi kiểm tra đối tượng Message tôi có thể thấy đối tượng Connection (là đối tượng nằm trong trường Tag) sẽ xóa s ngày (Main Chủ đề)

Nếu bạn nhìn vào bức tranh này, bạn sẽ thấy nó rõ ràng như ban ngày:

Weird thread behavior

Bạn có thể nhìn thấy nơi tôi đánh dấu bằng hộp màu xanh lá cây, tôi cố gắng đọc thuộc tính message.Tag theo ba cách khác nhau, tất cả đều trả về null như bạn có thể thấy trong phần được đánh dấu bằng một ô màu xanh lam.

Tuy nhiên, nếu bạn nhìn vào hai khu vực được đánh dấu là màu đỏ, bạn có thể thấy rõ ràng là ngày mà đối tượng thực sự tồn tại. Và, chỉ để xóa mọi sự nhầm lẫn, phần mà thư được đưa vào hàng đợi thư đã nhận như sau:

Tôi có thể thấy tôi thậm chí đã thử thực hiện một Thread.VolatileWrite để đảm bảo giá trị được ghi

message.Tag = buffer.Tag; 

Thread.VolatileWrite(ref message.Tag, buffer.Tag); 

if (message.Tag == null) 
{ 
    isNullLog.Add(message.Id); 
} 

// Queue into received messages 
lock (peer.ReceivedMessages) 
{ 
    peer.ReceivedMessages.Enqueue(message); 
} 

đoạn trên là tất cả xảy ra trong các sợi nhân, và như bạn có thể thấy tôi sao chép buffer.Tag qua message.Tag, tôi thậm chí thiết lập một tấm séc runtime chút để gỡ lỗi mà kiểm tra message.Tag cho một giá trị null và thêm id của nó vào danh sách được gọi là "isNullLog" nếu đúng như vậy. Khi NullReferenceException bị ném vào luồng chính, danh sách này trống.

Bạn cũng thấy rằng tôi khóa hàng đợi peer.ReceivedMessages và đẩy thư đến hàng đợi sau tôi đã đặt trường message.Tag.

Ngoài ra, để thậm chí còn rõ ràng ở đây hơn là chức năng được sử dụng để đọc tin nhắn ra khỏi peer.ReceivedMessages đợi:

public bool TryGetMessage(out TIncomingMessage message) 
{ 
    lock (ReceivedMessages) 
    { 
     if (ReceivedMessages.Count > 0) 
     { 
      message = ReceivedMessages.Dequeue(); 
      return true; 
     } 
    } 

    ReceivedMessageEvent.Reset(); 

    message = null; 
    return false; 
} 

Bạn có thể thấy rằng tôi khóa hàng đợi ngay cả trước khi tôi kiểm tra số lượng, và nếu nó không có sản phẩm nào, tôi đặt thuộc tính ra ngoài và trả về true, nếu không tôi sẽ trả về false.

Thành thật mà nói, tôi hoàn toàn bối rối, viết một số ứng dụng đa luồng trước và chưa bao giờ gặp phải điều này.

Một chút cập nhật, tôi cũng đã thử đánh dấu trường Tagvolatile, làm cho nó trông như thế này public volatile object Tag; nhưng điều này có vẻ không giúp ích gì.

+2

Tôi không phải là một học giả, Nhưng tôi nghĩ rằng bạn đang trộn ổ khóa với đọc dễ bay hơi và viết .. Tôi nghĩ rằng đây là vấn đề. Nếu bạn sử dụng dễ bay hơi, bạn nên sử dụng nó tốt hơn .. Nếu bạn đang sử dụng ổ khóa thì chỉ khóa .. –

+0

Hm, không chắc chắn nếu tôi đồng ý - Tôi có khóa nơi tất cả các chủ đề đáp ứng, phần dễ bay hơi dường như không có hiệu lực nó ở tất cả (như trong không có gì xảy ra nếu tôi loại bỏ nó hoặc thêm nó). – thr

+1

Có chuỗi nào khác hoạt động trên trường 'message.Tag' (ví dụ: đặt nó thành null) không? – Hans

Trả lời

2

Tôi đã thực sự sửa lỗi này ngay bây giờ, như mọi khi đối phó với các chủ đề bạn cần phải cẩn thận khi đọc/ghi các giá trị. Tôi đã quên xóa biến cục bộ message trong vòng lặp nhận và kết thúc "sử dụng lại" cùng một thông báo trong vòng lặp tiếp theo, vì nó có kiểm tra if(message == null) { /* create new message */ } trước mỗi lần lặp và khi tôi không xóa chủ đề đọc này đã kết thúc chà đạp trên tất cả các tin nhắn "cũ" đã được lưu trữ ở đây khi cố gắng để viết một tin nhắn mới cho nó!

+1

Bạn có thể có quá nhiều kiểm tra :) Đừng bận tâm kiểm tra hoặc xóa thông điệp biến đối tượng - chỉ cần tham gia vào thói quen tạo một thể hiện mới lúc bắt đầu và như bước tiếp theo sau khi xếp hàng một lần. –

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