Quản lý các tác vụ không đồng bộ và song song

Đã hoàn thành

Đối với nhà phát triển C#, Thư viện Nhiệm vụ Song song (TPL) cung cấp một cách dễ dàng hơn để viết mã song song. Tuy nhiên, không phải tất cả các mã đều phù hợp cho việc song song. Ví dụ: nếu một vòng lặp chỉ thực hiện một lượng công việc nhỏ trên mỗi lần lặp hoặc vòng lặp đó không chạy cho nhiều lần lặp, thì việc tính tổng phí song song có thể khiến mã chạy chậm hơn. Hơn nữa, song song hóa, giống như bất kỳ mã đa đọc nào, làm cho việc thực thi chương trình của bạn thêm phức tạp.

Các cạm bẫy phổ biến trong dữ liệu và tính song song của nhiệm vụ

Trong nhiều trường hợp, và Parallel.ForParallel.ForEach thể cung cấp những cải tiến hiệu suất đáng kể hơn các vòng lặp tuần tự thông thường. Tuy nhiên, công việc song song vòng lặp mang lại sự phức tạp có thể dẫn đến các vấn đề không phổ biến trong mã tuần tự.

Đừng giả định rằng song song luôn nhanh hơn

Trong một số trường hợp, một vòng lặp song song có thể chạy chậm hơn lần tương đương tuần tự của nó. Quy tắc cơ bản của ngón cái là các vòng lặp song song có ít lần lặp và đại diện người dùng nhanh không thể tăng tốc nhiều. Tuy nhiên, vì nhiều yếu tố liên quan đến hiệu suất, chúng tôi khuyên bạn nên luôn đo lường kết quả thực tế.

Tránh ghi vào vị trí bộ nhớ dùng chung

Trong mã tuần tự, việc đọc từ hoặc ghi vào các biến tĩnh hoặc trường lớp không phải là không phổ biến. Tuy nhiên, bất cứ khi nào nhiều chủ đề đang truy cập các biến như vậy cùng một lúc, có một tiềm năng đáng kể cho các điều kiện chủng tộc. Mặc dù bạn có thể sử dụng khóa để đồng bộ hóa truy nhập vào biến, chi phí đồng bộ hóa có thể ảnh hưởng đến hiệu suất. Do đó, chúng tôi khuyên bạn nên tránh hoặc ít nhất là giới hạn quyền truy cập vào trạng thái chia sẻ trong một vòng lặp song song càng nhiều càng tốt. Cách tốt nhất để làm điều này là sử dụng quá tải Parallel.ForParallel.ForEachSystem.Threading.ThreadLocal<T> và sử dụng một biến để lưu trữ chủ đề địa phương nhà nước trong khi thực hiện vòng lặp.

Tránh song song quá

Bằng cách sử dụng vòng lặp song song, bạn phải chịu chi phí chi phí phân vùng bộ sưu tập nguồn và đồng bộ hóa các chủ đề nhân viên. Lợi ích của song song hóa được giới hạn thêm bởi số lượng các bộ xử lý trên máy tính. Không có tăng tốc để đạt được bằng cách chạy nhiều chủ đề liên quan đến tính toán trên chỉ một bộ xử lý. Vì vậy, bạn phải cẩn thận không để over-parallelize một vòng lặp.

Kịch bản phổ biến nhất mà trong đó sự song song hóa có thể xảy ra là trong các vòng lặp lồng nhau. Trong hầu hết các trường hợp, tốt nhất là chỉ nên song song vòng lặp bên ngoài trừ khi áp dụng một hoặc nhiều điều kiện sau đây:

  • Vòng lặp bên trong được biết là dài.
  • Bạn đang thực hiện một tính toán đắt tiền trên mỗi đơn hàng.
  • Hệ thống mục tiêu được biết là có đủ bộ xử lý để xử lý số lượng các chủ đề được sản xuất bằng cách song song hóa việc xử lý.

Trong mọi trường hợp, cách tốt nhất để xác định hình dạng truy vấn tối ưu là kiểm tra và đo lường.

Xử lý ngoại lệ trong các tác vụ không đồng bộ và song song

Khi bạn sử dụng Thư viện Nhiệm vụ Song song (TPL) để chạy tác vụ, ngoại lệ có thể xảy ra theo một số cách khác nhau. Phổ biến nhất là khi một tác vụ trả về một ngoại lệ. Ném một ngoại lệ có thể xảy ra khi nhiệm vụ đang chạy trên một chủ đề vùng chủ đề hoặc khi nó đang chạy trên chủ đề chính. Trong cả hai trường hợp, ngoại lệ được phát tán trở lại chuỗi cuộc gọi.

Khi bạn sử dụng phương Task.Wait pháp để chờ cho một nhiệm vụ để hoàn thành, bất kỳ ngoại lệ đã được ném bởi nhiệm vụ được phát tán trở lại để các chủ đề gọi điện thoại. Bạn có thể xử lý các ngoại lệ này bằng cách sử dụng một khối try/catch. Nếu nhiệm vụ là nhiệm vụ mẹ của các nhiệm vụ con đính kèm hoặc nếu bạn đang chờ nhiều nhiệm vụ, có thể loại bỏ nhiều ngoại lệ. Nếu có một hoặc nhiều ngoại lệ được đưa ra, chúng sẽ được ngắt dòng trong một phiên AggregateException bản.

Ngoại AggregateException lệ có một InnerExceptions thuộc tính có thể được liệt kê để kiểm tra tất cả các ngoại lệ ban đầu đã được ném, và xử lý (hoặc không xử lý) mỗi một cá nhân.

Ví dụ sau đây minh họa cách xử lý các ngoại lệ do tác vụ trả về.


public static partial class Program
{
    public static void Main()
    {
        HandleThree();
    }
    
    public static void HandleThree()
    {
        var task = Task.Run(
            () => throw new CustomException("This exception is expected!"));

        try
        {
            task.Wait();
        }
        catch (AggregateException ae)
        {
            foreach (var ex in ae.InnerExceptions)
            {
                // Handle the custom exception.
                if (ex is CustomException)
                {
                    Console.WriteLine(ex.Message);
                }
                // Rethrow any other exception.
                else
                {
                    throw ex;
                }
            }
        }
    }
}

// Define the CustomException class
public class CustomException : Exception
{
    public CustomException(string message) : base(message) { }
}
// The example displays the following output:
//        This exception is expected!

Trong ví dụ này, HandleThree phương pháp sẽ tạo tác vụ trả về CustomException. Khối try/catch bắt gặp và lặp AggregateException lại qua bộ sưu InnerExceptions tập. Nếu ngoại lệ thuộc loại CustomException, hệ thống sẽ in thông báo tới bảng điều khiển. Nếu nó là bất kỳ loại khác của ngoại lệ, nó rethrows nó.

Bạn cũng có thể xử lý các ngoại lệ ban đầu bằng cách sử dụng phương AggregateException.Handle pháp. Phương pháp này lấy một đại diện được gọi cho mỗi ngoại lệ trong bộ sưu InnerExceptions tập. Nếu người đại diện trả về true, ngoại lệ được coi là đã xử lý và được loại bỏ khỏi bộ sưu tập. Nếu nó trả về false, ngoại lệ là rethrown.

Ví dụ sau đây chứng minh làm thế nào để sử dụng phương Handle pháp để xử lý ngoại lệ ném bởi một nhiệm vụ.


public static partial class Program
{
    public static void HandleFour()
    {
        var task = Task.Run(
            () => throw new CustomException("This exception is expected!"));

        try
        {
            task.Wait();
        }
        catch (AggregateException ae)
        {
            ae.Handle(ex =>
            {
                // Handle the custom exception.
                if (ex is CustomException)
                {
                    Console.WriteLine(ex.Message);
                    return true;
                }
                // Rethrow any other exception.
                return false;
            });
        }
    }
}

Trong ví dụ này, HandleFour phương pháp sẽ tạo tác vụ trả về CustomException. Khối try/catch bắt và gọi AggregateException các phương Handle pháp. Người đại diện kiểm tra xem ngoại lệ có phải là loại .CustomException Nếu ngoại lệ thuộc loại , người CustomExceptionđại diện sẽ in thư đến bảng điều khiển và trả về true. Phản hồi cho true biết ngoại lệ đã được xử lý. Nếu ngoại lệ là bất kỳ loại ngoại lệ nào khác, người đại falsediện sẽ trả về , khiến ngoại lệ được rethrown.

Tóm tắt

Đơn vị này mô tả các tình huống khi mã không phù hợp cho song song và thảo luận về các cạm bẫy phổ biến trong dữ liệu và song song nhiệm vụ. Ví dụ: giả sử song song luôn nhanh hơn, viết vào các vị trí bộ nhớ dùng chung và quá song song. Nội dung cũng giải thích cách xử lý ngoại lệ trong các tác vụ không đồng bộ và song song, bao gồm cách sử dụng phương Task.Wait pháp và phương AggregateException.Handle pháp.

Các điểm chính

  • Không phải tất cả các mã đều phù hợp cho việc song song. Hiệu suất kiểm tra và đo lường là điều cần thiết trước khi song song mã.
  • Các cạm bẫy phổ biến trong dữ liệu và song song nhiệm vụ bao gồm giả định song song luôn nhanh hơn, ghi vào vị trí bộ nhớ dùng chung và song song hóa quá mức.
  • Có thể xử lý ngoại lệ trong các tác vụ không đồng bộ và song song bằng phương Task.Wait pháp và phương AggregateException.Handle pháp.
  • Ngoại AggregateException lệ có một InnerExceptions thuộc tính có thể được liệt kê để kiểm tra tất cả các ngoại lệ ban đầu đã được ném.