2012-03-29 24 views
8

Tôi đã ứng dụng multithreads và tôi nhận được lỗi nàyBộ sưu tập đã được sửa đổi, hoạt động liệt kê không thể thực hiện

************** Exception Text ************** 
System.InvalidOperationException: Collection was modified; enumeration operation may not execute. 
    at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) 
    at System.Collections.Generic.List`1.Enumerator.MoveNextRare() 
    at System.Collections.Generic.List`1.Enumerator.MoveNext() 
    ... 

tôi có lẽ có vấn đề với bộ sưu tập của tôi, bởi vì trên một thread tôi đọc bộ sưu tập của tôi và trên thread khác tôi sửa đổi bộ sưu tập.

public readonly ObservableCollectionThreadSafe<GMapMarker> Markers = new ObservableCollectionThreadSafe<GMapMarker>(); 


public void problem() 
{ 
    foreach (GMapMarker m in Markers) 
    { 
    ... 
    } 
} 

Tôi đang cố khóa bộ sưu tập bằng mã này nhưng không hoạt động.

public void problem() 
    { 
     lock(Markers) 
     { 
     foreach (GMapMarker m in Markers) 
     { 
      ... 
     } 
     } 
    } 

Bất kỳ ý tưởng nào để khắc phục sự cố đó?

+1

Bạn vấn đề là với mã bên trong 'foreach', xin vui lòng gửi nó. – nemesv

+3

bạn không thể sửa đổi bộ sưu tập trong khi lặp với foreach – Reniuz

Trả lời

4

Bạn cần khóa cả trên mặt đọc và mặt viết. Nếu một trong những chủ đề sẽ không biết về các khóa và sẽ cố gắng để đọc/sửa đổi chế độ thu, trong khi người kia được chỉnh sửa/đọc (tương ứng) với khóa được tổ chức

4

Cố gắng đọc một bản sao của bộ sưu tập của bạn

foreach (GMapMarker m in Markers.Copy()) 
{ 
    ... 
} 

điều này sẽ tạo một bản sao mới của bộ sưu tập của bạn sẽ không bị ảnh hưởng bởi một chuỗi khác nhưng có thể gây ra vấn đề hiệu suất trong trường hợp bộ sưu tập khổng lồ.

Vì vậy, tôi nghĩ sẽ tốt hơn nếu bạn khóa bộ sưu tập trong khi đọc và viết quy trình.

+0

... và sửa đổi bộ sưu tập gốc. – Reniuz

+0

bạn là đúng, tôi nghĩ rằng để sử dụng '.Copy' nhưng nó có thể gây ra một vấn đề hiệu suất. –

8

Đây là lỗi khá phổ biến - sửa đổi một bộ sưu tập trong khi lặp lại nó bằng cách sử dụng foreach, xin lưu ý rằng foreach sử dụng chỉ đọc IEnumerator ví dụ.

Thử lặp qua các bộ sưu tập sử dụng for() với kiểm tra chỉ số thêm vì vậy nếu chỉ số này ra khỏi ràng buộc - bạn sẽ có thể áp dụng logic thêm để xử lý này, cũng như tình trạng thoát vòng lặp bạn có thể sử dụng LINQ Count() đó sẽ đánh giá giá trị đếm mỗi lần nếu liệt kê bên dưới không thực hiện ICollection:

Nếu Markers cụ IColletion - khóa trên SyncRoot:

lock (Markers.SyncRoot) 

Sử dụng for():

for (int index = 0; index < Markers.Count(); index++) 
{ 
    if (Markers>= Markers.Count()) 
    { 
     // TODO: handle this case to avoid run time exception 
    } 
} 

Có thể tìm thấy bài này hữu ích: How do foreach loops work in C#?

+0

nhưng nếu sửa đổi bằng cách xóa một mục khỏi bộ sưu tập, điều đó sẽ ném một ngoại lệ 'IndexOutOfRange' –

+1

Tôi đã đề cập đến kiểm tra chỉ mục bổ sung để tránh vấn đề này, sẽ thêm mẫu, cảm ơn vì đã trỏ đến số này – sll

+0

nhưng sau đó tôi nghĩ nếu sẽ tốt hơn để khóa bộ sưu tập nhưng không hoạt động:/ – PATO7

0

Bạn có thể sử dụng một foreach nhưng bạn phải bỏ việc thu thập vào một danh sách và sử dụng toán tử dấu chấm để truy cập các phương pháp hành vi.

Ví dụ: foreach Markers.Tolist() (i => i.DeleteObject())

Không hoàn toàn chắc chắn những gì bạn đang làm với bộ sưu tập của bạn.. Ví dụ của tôi là giả sử bạn chỉ muốn xóa tất cả các mục khỏi bộ sưu tập, nhưng nó có thể được áp dụng cho bất kỳ hành vi nào bạn đang cố gắng thực hiện với bộ sưu tập của mình.

0

Tôi khuyên bạn nên sử dụng AsyncCommand, vì AsyncCommand được thực hiện hoặc không, trong khi sử dụng lock(Markers) cho phép tái sử dụng.(Xem https://github.com/StephenCleary/AsyncEx/wiki/AsyncLock):

private readonly AsyncLock _markersMutex = new AsyncLock(); 

    using (await _markersMutex.LockAsync().ConfigureAwait(false)) 
    { 
    foreach (GMapMarker m in Markers) 
    { 
     ... 
    } 
    } 

Ngoài ra, AsyncLock cho phép bạn thay thế Thread.Sleep với async tương đương của nó, await Task.Delay(TimeSpan.FromSeconds(1))

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