Parallel.ForEach với việc thêm vào danh sách


80

Tôi đang cố gắng chạy nhiều chức năng kết nối với một trang web từ xa (theo mạng) và trả về một danh sách chung. Nhưng tôi muốn chạy chúng đồng thời.

Ví dụ:

public static List<SearchResult> Search(string title)
{
    //Initialize a new temp list to hold all search results
    List<SearchResult> results = new List<SearchResult>();

    //Loop all providers simultaneously
    Parallel.ForEach(Providers, currentProvider =>
    {
        List<SearchResult> tmpResults = currentProvider.SearchTitle((title));

        //Add results from current provider
        results.AddRange(tmpResults);
    });

    //Return all combined results
    return results;
}

Như tôi thấy, nhiều lần chèn vào 'kết quả' có thể xảy ra cùng lúc ... Điều này có thể làm ứng dụng của tôi bị hỏng.

Làm thế nào tôi có thể tránh điều này?


Bạn đang sử dụng phiên bản .NET nào?
sll

4
Nó sẽ phải ít nhất là .Net 4; Song song đã được giới thiệu ở đó.
Matt Mills

Câu trả lời:


58
//In the class scope:
Object lockMe = new Object();    

//In the function
lock (lockMe)
{    
     results.AddRange(tmpResults);
}

Về cơ bản, khóa có nghĩa là chỉ một luồng có thể truy cập vào phần quan trọng đó cùng một lúc.


1
Nhưng điều gì sẽ xảy ra nếu WHILE những kết quả đó được thêm vào kết quả từ một nhà cung cấp khác cố gắng thêm vào? họ sẽ THẤT BẠI hay CHỜ cho đến khi có thể?
shaharmor

4
Khi có khóa, chủ đề sẽ đợi cho đến khi có thể lấy được khóa.
Haedrian

Vì vậy, về cơ bản nó giống như nói: Chờ cho đến khi kết quả.isLocked, và khi nó miễn phí khóa nó và viết?
shaharmor

8
Một điểm nhỏ: thiskhông phải là sự lựa chọn an toàn nhất cho đối tượng khóa. Tốt hơn để sử dụng một đối tượng riêng đặc biệt: lock(resultsLock).
Henk Holterman

2
lockscó thể làm chậm thời gian thực hiện tổng thể mặc dù .. bộ sưu tập đồng thời có vẻ tốt hơn để tránh điều đó
Piotr Kula

155

Bạn có thể sử dụng một tập hợp đồng thời .

Không System.Collections.Concurrentgian tên cung cấp một số lớp thu thập an toàn luồng sẽ được sử dụng thay cho các kiểu tương ứng trong không gian tên System.CollectionsSystem.Collections.Genericbất cứ khi nào nhiều luồng truy cập đồng thời vào tập hợp.

Ví dụ, bạn có thể sử dụng ConcurrentBagvì bạn không có đảm bảo thứ tự các mặt hàng sẽ được thêm vào.

Đại diện cho một tập hợp các đối tượng an toàn theo chuỗi, không có thứ tự.


Vâng, đây là câu trả lời thực tế. Bạn sẽ nhận được hiệu suất tốt hơn (nói chung) với các bộ sưu tập đồng thời.
lkg

32

Đối với những người thích mã:

public static ConcurrentBag<SearchResult> Search(string title)
{
    var results = new ConcurrentBag<SearchResult>();
    Parallel.ForEach(Providers, currentProvider =>
    {
        results.Add(currentProvider.SearchTitle((title)));
    });

    return results;
}

Phải sử dụng vòng lặp: foreach (var item in currentProvider.SearchTitle((title))) results.Add(item);
Anthony McGrath

25

Các Bộ sưu tập Đồng thời là mới cho .Net 4; chúng được thiết kế để hoạt động với chức năng song song mới.

Xem Bộ sưu tập đồng thời trong .NET Framework 4 :

Trước .NET 4, bạn phải cung cấp cơ chế đồng bộ hóa của riêng mình nếu nhiều luồng có thể đang truy cập một bộ sưu tập được chia sẻ. Bạn đã phải khóa bộ sưu tập ...

... các lớp và giao diện [mới] trong System.Collections.Concurrent [được thêm vào .NET 4] cung cấp triển khai nhất quán cho [...] các vấn đề lập trình đa luồng liên quan đến dữ liệu được chia sẻ trên các luồng.


14

Điều này có thể được diễn đạt ngắn gọn bằng cách sử dụng PLINQ AsParallelSelectMany:

public static List<SearchResult> Search(string title)
{
    return Providers.AsParallel()
                    .SelectMany(p => p.SearchTitle(title))
                    .ToList();
}

linq selectMany là tuyệt vời, đáng buồn là linq chậm hơn foreach bình thường. :(
Kugan Kumar

6
Đừng tối ưu hóa vi mô. OP ngụ ý rằng SearchTitlekết nối với một trang web từ xa. Độ trễ của nó sẽ chậm hơn vài bậc so với sự khác biệt giữa LINQ và foreach.
Douglas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.