2011-11-21 9 views
6

Tôi đã đọc về các Task Parallel Library và bài viết nói:Nên .Net 4.0 Nhiệm vụ luôn là phương pháp ưu tiên cho các ứng dụng đa luồng?

Trong .NET Framework 4, nhiệm vụ là API ưa thích cho các văn bản đa luồng, không đồng bộ, và mã song song

Nhưng nó cũng nói rằng họ sử dụng ThreadPool đằng sau hậu trường. Những gì tôi gặp khó khăn trong việc tìm ra là nhiệm vụ chỉ nên được sử dụng khi bạn sử dụng ThreadPool (và vì vậy "Thread vs. Task" sẽ tương đương với "Thread so ThreadPool"), hoặc nếu Microsoft dự định sử dụng Tasks bất cứ nơi nào nhiều chủ đề được yêu cầu, mà không có những cân nhắc vốn có cho tình trạng tiến thoái lưỡng nan "Thread vs. ThreadPool".

Vì vậy, các Công việc có nên được sử dụng ở bất kỳ nơi nào có nhiều chủ đề được yêu cầu không?

Trả lời

4

Lợi thế thiết kế của việc sử dụng các tác vụ là bạn chuyển giao phần cứng của luồng cho thời gian chạy, có thể hoàn thành nhiệm vụ luồng bằng cách sử dụng một giải pháp ít lỗi hơn, tối ưu hơn. Tôi biết một số mô hình dựa trên nhiệm vụ, chẳng hạn như PLINQ, cho phép bạn gợi ý chiến lược mà thời gian chạy nên áp dụng, để câu hỏi "Threadpool hoặc không Threadpool" có thể được xử lý trực tiếp.

Việc chuyển sang mô hình này tương tự như chuyển sang ngôn ngữ được quản lý GC-ed so với ngôn ngữ yêu cầu bạn làm sạch bộ nhớ của riêng bạn. Sẽ luôn có những lập luận ủng hộ cho thứ hai, nhưng Garbage Collection giờ đây đã được tối ưu hóa đến mức thực tế nó không phải là vấn đề. Lý tưởng nhất, cơ chế chuyển đổi thời gian chạy cho các nhiệm vụ sẽ phát triển và trở nên tốt hơn. Vì vậy, về mặt lý thuyết, ứng dụng của bạn được viết và biên dịch cho .NET 4 có thể nhanh hơn với việc triển khai tốt hơn thời gian chạy mà không cần biên dịch lại. Ngoài ra, mã luồng là nổi tiếng khó có được quyền, vì vậy bất kỳ cơ chế nào ẩn các chi tiết đó đều tốt cho lập trình viên.

Liệu những lợi ích đó có lớn hơn các nguy cơ tiềm tàng, chẳng hạn như các trường hợp cạnh mà thời gian chạy không xử lý tốt, là điều cần được xem xét từng trường hợp. Tôi chắc chắn sẽ cố gắng không tối ưu hóa sớm ở đây, mặc dù.

+0

Đó là sự hiểu biết của tôi rằng các tác vụ chạy dài phù hợp nhất cho các chủ đề, trong khi các tác vụ ngắn hơn thì tốt hơn cho ThreadPool. Ngoài ra còn có vấn đề về nền trước so với nền. Tác vụ có thực sự tìm hiểu xem chức năng được truyền trong có chạy dài hay không hoặc liệu nó có phải là tiền cảnh hoặc nền không? Tôi không thấy nó có thể như thế nào. Vậy làm thế nào nó có thể hỏi liệu một ThreadPool là tuyến đường tốt nhất hay không? Và nếu nó luôn luôn sử dụng một ThreadPool, một trong những nên tiếp tục sử dụng một thread khi ThreadPool là không thích hợp? – Bob

+0

Bạn nói đúng, nó không thể biết được nhiệm vụ có dài hạn hay không, đó là vấn đề dừng lại. Từ những gì tôi hiểu, thời gian chạy có thể xác định xem truy vấn hoặc chức năng của bạn có bị ràng buộc tính toán hay ràng buộc IO hay không, và nó chọn chiến lược phù hợp. – cunningdave

+0

Không thấy độ dài của tác vụ liên quan đến câu hỏi Threadpool. Mục đích của Threadpool là giảm thiểu chi phí của việc tạo và giết các luồng bằng cách sử dụng lại phân bổ luồng với các nhiệm vụ tùy ý. Nhưng Threadpool được thực hiện bằng cách sử dụng Threads, được quản lý bởi một trọng tài viên. Tôi không chắc chắn nếu bàn giao một nhiệm vụ lâu dài cho một hồ bơi thực sự là một thiệt hại, khác hơn là có lẽ làm thế nào nó xử lý việc quản lý thread. Quay lại Q, thời gian chạy có thể chọn một cách tiếp cận tinh vi hơn tùy thuộc vào những gì nó xác định là chiến lược tốt nhất. tức là bạn có thể thực hiện Asynch trên một luồng bằng cách sử dụng PostMessage. – cunningdave

1

Bạn có thể sử dụng TaskCreationOptions.LongRunning làm gợi ý cho TPL biết rằng nhiệm vụ của bạn có thể liên quan nhiều hơn những gì mà ThreadPool được điều chỉnh. Nhưng, vâng, TPL dường như là phương pháp ưa thích cho lập trình đa luồng nhìn về phía trước. Microsoft thậm chí còn xây dựng trên đầu trang của nó để hỗ trợ các từ khóa asyncawait mới được đề xuất trong Async CTP. Điều đó không có nghĩa là bạn phải từ bỏ các kiểu cũ ThreadThreadPool APIs. Tuy nhiên, cá nhân tôi nhận thấy rằng TPL thực hiện hầu hết những gì tôi muốn trong một API thanh lịch hơn và tôi có xu hướng dựa vào nó hầu như bây giờ.

0

Nhiệm vụ là mức trừu tượng cao hơn so với luồng hoặc ThreadPool. Về cơ bản bạn đóng gói một chức năng trong một nhiệm vụ và yêu cầu thời gian chạy để thực hiện nó tốt nhất có thể. Bạn có thể có nhiều hàng chục nếu không phải là các nhiệm vụ sẽ được thực hiện bởi một số lượng hạn chế các chủ đề.

Sử dụng các tác vụ mà nhà phát triển tạo ra bao nhiêu tác vụ khi cần, nối chúng để tạo luồng (luồng công việc trong F #) và kiểm soát việc hủy của chúng mà không làm phiền đến cách phân bổ hoặc sử dụng luồng. Đó là vào thời gian chạy để chọn cách tốt nhất để thực hiện tất cả các nhiệm vụ bằng cách sử dụng số lượng hạn chế của chủ đề.

Công việc giúp việc triển khai các mẫu lập trình đồng thời dễ dàng hơn nhiều.Thư viện ParallelExtensionExtras cung cấp các phương thức để chuyển đổi cặp Begin/EndXXX thành các tác vụ có thể được ghép và một phiên bản sơ bộ của thành phần Lặp lại nhiệm vụ được sử dụng bởi Async CTP để cung cấp cú pháp async/await. Bạn có thể sử dụng ConcurrentCollection của các tác vụ để tạo hàng đợi công việc, tương tự như ví dụ AsyncCall trong ParallelExtensionExtras, hoặc đi xa hơn và tạo các tác nhân tương tự như Scala, Erlang hoặc F #. DataFlow trong CTP Async là một ví dụ khác về những gì bạn có thể tạo bằng các tác vụ.

Bạn phải ghi nhớ rằng trong khi một máy tính xách tay điển hình có 2 lõi và một máy tính để bàn điển hình có 4, máy chủ nhỏ đã có 8 lõi trở lên và chẳng bao lâu nữa chúng sẽ có nhiều hơn. Giữ tất cả những lõi bận rộn bằng cách lập lịch trình thủ công và tránh các khối có thể trở thành một nhức đầu lớn.