Đưa ra mã này:
var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
DoSomething(someString);
});
Tất cả 1000 chủ đề sẽ sinh ra gần như đồng thời?
Đưa ra mã này:
var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
DoSomething(someString);
});
Tất cả 1000 chủ đề sẽ sinh ra gần như đồng thời?
Câu trả lời:
Không, nó sẽ không bắt đầu 1000 chủ đề - vâng, nó sẽ giới hạn số lượng chủ đề được sử dụng. Tiện ích mở rộng song song sử dụng số lượng lõi thích hợp, dựa trên số lượng lõi bạn có và bao nhiêu lõi đã bận. Nó phân bổ công việc cho mỗi lõi và sau đó sử dụng một kỹ thuật được gọi là đánh cắp công việc để cho phép mỗi luồng xử lý hàng đợi của chính nó một cách hiệu quả và chỉ cần thực hiện bất kỳ truy cập xuyên luồng đắt tiền nào khi nó thực sự cần.
Hãy xem Blog của Nhóm PFX để biết vô số thông tin về cách nó phân bổ công việc và tất cả các loại chủ đề khác.
Lưu ý rằng trong một số trường hợp, bạn cũng có thể chỉ định mức độ song song mà bạn muốn.
Trên một máy lõi đơn ... Parallel.Mỗi phân vùng (phần) của bộ sưu tập mà nó đang hoạt động giữa một số luồng, nhưng con số đó được tính toán dựa trên một thuật toán có tính đến và dường như liên tục theo dõi công việc được thực hiện bởi luồng nó đang phân bổ cho ForEach. Vì vậy, nếu phần nội dung của ForEach gọi ra các hàm IO-ràng buộc / chặn đang chạy dài sẽ khiến chuỗi chờ xung quanh, thuật toán sẽ tạo ra nhiều chuỗi hơn và phân vùng lại bộ sưu tập giữa chúng . Nếu các chuỗi hoàn thành nhanh chóng và không chặn trên các chuỗi IO, chẳng hạn như chỉ cần tính toán một số số,thuật toán sẽ tăng (hoặc thực sự giảm) số luồng đến một điểm mà thuật toán coi là tối ưu cho thông lượng (thời gian hoàn thành trung bình của mỗi lần lặp) .
Về cơ bản nhóm luồng đằng sau tất cả các chức năng thư viện Song song khác nhau, sẽ tính ra số lượng luồng tối ưu để sử dụng. Số lượng lõi xử lý vật lý chỉ là một phần của phương trình. KHÔNG có mối quan hệ đơn giản 1-1 giữa số lõi và số luồng được tạo ra.
Tôi không thấy tài liệu về việc hủy và xử lý đồng bộ hóa các luồng rất hữu ích. Hy vọng rằng MS có thể cung cấp các ví dụ tốt hơn trong MSDN.
Đừng quên, mã phần thân phải được viết để chạy trên nhiều luồng, cùng với tất cả các cân nhắc về an toàn luồng thông thường, khung công tác chưa tóm tắt yếu tố đó ...
Nó tính toán số luồng tối ưu dựa trên số lượng bộ xử lý / lõi. Chúng sẽ không sinh sản cùng một lúc.
Xem Liệu Song song. Để sử dụng một Tác vụ mỗi lần lặp? để có ý tưởng về một "mô hình tinh thần" để sử dụng. Tuy nhiên, tác giả nói rằng "Vào cuối ngày, điều quan trọng cần nhớ là chi tiết triển khai có thể thay đổi bất cứ lúc nào."
Câu hỏi tuyệt vời. Trong ví dụ của bạn, mức độ song song là khá thấp ngay cả trên bộ vi xử lý lõi tứ, nhưng với một số chờ đợi, mức độ song song có thể khá cao.
// Max concurrency: 5
[Test]
public void Memory_Operations()
{
ConcurrentBag<int> monitor = new ConcurrentBag<int>();
ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
monitor.Add(monitor.Count);
monitor.TryTake(out int result);
monitorOut.Add(result);
});
Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}
Bây giờ hãy xem điều gì sẽ xảy ra khi một thao tác chờ được thêm vào để mô phỏng một yêu cầu HTTP.
// Max concurrency: 34
[Test]
public void Waiting_Operations()
{
ConcurrentBag<int> monitor = new ConcurrentBag<int>();
ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
monitor.Add(monitor.Count);
System.Threading.Thread.Sleep(1000);
monitor.TryTake(out int result);
monitorOut.Add(result);
});
Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}
Tôi chưa thực hiện bất kỳ thay đổi nào và mức độ đồng thời / song song đã tăng một cách kịch tính. Đồng tiền có thể có giới hạn của nó tăng lên với ParallelOptions.MaxDegreeOfParallelism
.
// Max concurrency: 43
[Test]
public void Test()
{
ConcurrentBag<int> monitor = new ConcurrentBag<int>();
ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
var arrayStrings = new string[1000];
var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
Parallel.ForEach<string>(arrayStrings, options, someString =>
{
monitor.Add(monitor.Count);
System.Threading.Thread.Sleep(1000);
monitor.TryTake(out int result);
monitorOut.Add(result);
});
Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}
// Max concurrency: 391
[Test]
public void Test()
{
ConcurrentBag<int> monitor = new ConcurrentBag<int>();
ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
var arrayStrings = new string[1000];
var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
Parallel.ForEach<string>(arrayStrings, options, someString =>
{
monitor.Add(monitor.Count);
System.Threading.Thread.Sleep(100000);
monitor.TryTake(out int result);
monitorOut.Add(result);
});
Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}
Tôi khuyên bạn nên thiết lập ParallelOptions.MaxDegreeOfParallelism
. Nó không nhất thiết phải tăng số lượng chủ đề được sử dụng, nhưng nó sẽ đảm bảo bạn chỉ bắt đầu một số lượng chủ đề lành mạnh, điều này dường như là mối quan tâm của bạn.
Cuối cùng để trả lời câu hỏi của bạn, không, bạn sẽ không nhận được tất cả các chủ đề để bắt đầu cùng một lúc. Sử dụng Parallel.Invoke nếu bạn đang muốn gọi song song một cách hoàn hảo, ví dụ: thử nghiệm các điều kiện cuộc đua.
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623368346
// 636462943623368346
// 636462943623373351
// 636462943623393364
// 636462943623393364
[Test]
public void Test()
{
ConcurrentBag<string> monitor = new ConcurrentBag<string>();
ConcurrentBag<string> monitorOut = new ConcurrentBag<string>();
var arrayStrings = new string[1000];
var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
Parallel.ForEach<string>(arrayStrings, options, someString =>
{
monitor.Add(DateTime.UtcNow.Ticks.ToString());
monitor.TryTake(out string result);
monitorOut.Add(result);
});
var startTimes = monitorOut.OrderBy(x => x.ToString()).ToList();
Console.WriteLine(string.Join(Environment.NewLine, startTimes.Take(10)));
}