2009-05-10 22 views
12

Có lẽ ai đó có thể chỉ cho tôi theo đúng hướng, bởi vì tôi hoàn toàn bối rối về điều này.Lạ "Bộ sưu tập đã được sửa đổi sau khi điều tra được khởi tạo" ngoại lệ

Tôi có một chức năng mà chỉ đơn giản in ra một LinkedList các lớp:

LinkedList<Component> components = new LinkedList<Component>(); 
    ... 
    private void PrintComponentList() 
    { 
     Console.WriteLine("---Component List: " + components.Count + " entries---"); 
     foreach (Component c in components) 
     { 
      Console.WriteLine(c); 
     } 
     Console.WriteLine("------"); 
    } 

Đối tượng Component thực sự có một phong tục gọi ToString() như vậy:

int Id; 
    ... 
    public override String ToString() 
    { 
     return GetType() + ": " + Id; 
    } 

Chức năng này thường hoạt động tốt - tuy nhiên Tôi đã gặp vấn đề khi nó xây dựng cho khoảng 30 mục nhập trong danh sách, báo cáo PrintcomplentListforeach quay trở lại với InvalidOperationException: Collection was modified after the enumerator was instantiated.

Bây giờ bạn có thể thấy tôi không sửa đổi mã trong vòng lặp for, và tôi đã không tạo ra bất kỳ chủ đề nào rõ ràng, mặc dù điều này nằm trong môi trường XNA (nếu nó quan trọng). Cần lưu ý rằng bản in thường xuyên đủ để đầu ra Console làm chậm toàn bộ chương trình.

Tôi hoàn toàn bối rối, có ai khác ngoài đó chạy vào điều này không?

+0

Điều đó nghe có vẻ kỳ lạ. Bạn có thể đăng một chương trình ngắn nhưng * hoàn chỉnh * để chúng tôi có thể thử tái tạo nó không? –

+0

Tôi sẽ thấy những gì tôi có thể làm. – cyberconte

+0

Tôi không có một chương trình nhỏ để tái tạo hành vi, vì vậy tôi sẽ xem xét về việc thực hiện an toàn thread của LinkedList trong đó để xem nó có bắt được bất cứ điều gì không? – cyberconte

Trả lời

11

Tôi nghi ngờ địa điểm bắt đầu tìm kiếm sẽ ở bất kỳ nơi nào bạn thao tác danh sách - tức là chèn/xóa/chuyển nhượng lại các mục. Nghi ngờ của tôi là sẽ có một callback/even-handler ở đâu đó đang bị sa thải không đồng bộ (có lẽ là một phần của vòng lặp XNA etc), và đó là chỉnh sửa danh sách - về cơ bản gây ra vấn đề này như một điều kiện chủng tộc.

Để kiểm tra xem trường hợp này có xảy ra một số kết quả gỡ lỗi/theo dõi xung quanh những nơi điều khiển danh sách hay không và đặc biệt là trước khi ngoại lệ chạy mã thao tác cùng một lúc giao diện điều khiển đầu ra của bạn:

private void SomeCallback() 
{ 
    Console.WriteLine("---Adding foo"); // temp investigation code; remove 
    components.AddLast(foo); 
    Console.WriteLine("---Added foo"); // temp investigation code; remove 
} 

Thật không may, những việc như vậy thường là một nỗi đau để gỡ lỗi, như thay đổi mã để điều tra nó thường thay đổi vấn đề (một Heisenbug).

Một câu trả lời là đồng bộ hóa quyền truy cập; tức là trong tất cả những nơi mà chỉnh sửa danh sách, sử dụng một lock xung quanh hoạt động hoàn chỉnh:

LinkedList<Component> components = new LinkedList<Component>(); 
readonly object syncLock = new object(); 
... 
private void PrintComponentList() 
{ 
    lock(syncLock) 
    { // take lock before first use (.Count), covering the foreach 
     Console.WriteLine("---Component List: " + components.Count 
       + " entries---"); 
     foreach (Component c in components) 
     { 
      Console.WriteLine(c); 
     } 
     Console.WriteLine("------"); 
    } // release lock 
} 

và gọi lại của bạn (hoặc bất kỳ)

private void SomeCallback() 
{ 
    lock(syncLock) 
    { 
     components.AddLast(foo); 
    } 
} 

Đặc biệt, một "hoàn thành hoạt động" có thể bao gồm:

  • kiểm tra số lượng foreach/for
  • kiểm tra cho sự tồn tại và chèn/xóa
  • vv

(ví dụ:không phải hoạt động cá nhân/rời rạc - nhưng đơn vị công việc)

+0

lưu ý rằng ghi lại id-thread khi bạn viết các đường theo dõi của bạn có thể giúp bạn xác định xem có chủ đề cạnh tranh hay không, và do đó khả năng của một cuộc đua. –

4

Thay vì foreach, tôi sử dụng while(collection.count >0) sau đó sử dụng collection[i].

2

Tôi không biết liệu điều này có liên quan đến OP hay không nhưng tôi đã gặp lỗi tương tự và tìm thấy chuỗi này trong khi tìm kiếm trên google. Tôi đã có thể giải quyết nó bằng cách thêm một break sau khi loại bỏ một phần tử trong vòng lặp.

foreach(Weapon activeWeapon in activeWeapons){ 

      if (activeWeapon.position.Z < activeWeapon.range) 
      { 
       activeWeapons.Remove(activeWeapon); 
       break; // Fixes error 
      } 
      else 
      { 
       activeWeapon.position += activeWeapon.velocity; 
      } 
     } 
    } 

Nếu bạn rời khỏi giờ nghỉ, bạn sẽ nhận được lỗi "InvalidOperationException: Collection đã được sửa đổi sau khi điều tra được khởi tạo".

+0

Điều này giới thiệu một lỗi khác, mặc dù - bất kỳ vũ khí nào của bạn trong danh sách sau khi vũ khí ngoài phạm vi đầu tiên sẽ không thay đổi vị trí của chúng! Tôi đã xử lý điều này trong quá khứ bằng cách tạo một Danh sách thứ hai để xóa khỏi danh sách đầu tiên, điền nó vào một foreach, sau đó thực hiện một danh sách thứ hai và xóa từng mục trong danh sách đầu tiên. – twon33

0

Sử dụng Break có thể là một cách nhưng nó có thể ảnh hưởng đến chuỗi hoạt động của bạn. Những gì tôi làm trong trường hợp đó trong chỉ đơn giản là chuyển đổi các foreach để truyền thống vòng lặp for

for(i=0;i<List.count;i++) 
{ 
List.Remove(); 
i--; 
} 

này hoạt động mà không cần bất kỳ vấn đề.

+0

Điều đó có thể hoạt động, nhưng trong trường hợp này, sự lặp lại là đối tượng của một bộ sưu tập. foreach (var obj trong collection.keys) ... – Futureproof

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