2012-04-22 53 views
7

Theo số MSDN Documentation TaskFactory.StartNew, hãy tạo và bắt đầu Tác vụ. Vì vậy, đối với các mẫu mã dưới đâyTại sao TaskFactory.StartNew Task không bắt đầu ngay lập tức?

class Program 
{ 
    public static void Main() 
    { 
     var t =Task.Factory.StartNew(
       () => SomeLongRunningCalculation(10, Display) 
      ); 
     var t1 = Task.Factory.StartNew(
       () => SomeLongRunningCalculation(20, Display) 
      ); 
     Console.WriteLine("Invoked tasks"); 
     Task.WaitAll(t, t1); 
     Console.ReadLine(); 
    } 

    public static void Display(int value) 
    { 
     Console.WriteLine(value); 
    } 

    public static void SomeLongRunningCalculation(int j, Action<int> callBack) 
    { 
     Console.WriteLine("Invoking calculation for {0}", j); 
     System.Threading.Thread.Sleep(1000); 
     if (callBack != null) 
     { 
      callBack(j + 1); 
     } 
    } 
}  

kết quả mong muốn của tôi là

Invoking calculation for 10 
Invoking calculation for 20 
Invoked tasks 
11 
21 

Tuy nhiên, nó được hiển thị

Invoked tasks 
Invoking calculation for 20 
Invoking calculation for 10 
21 
11 

Tôi muốn tìm hiểu

  1. Tại sao các tác vụ không chạy ngay sau StartNew?
  2. Tôi nên làm gì để có được kết quả theo định dạng mong muốn?

Trả lời

10

Đây là kết quả rất có khả năng trên một máy tính với một cpu lõi đơn. Hoặc có thể trên một với một cpu đa lõi và nó cũng bận rộn làm cái gì khác.

Tạo tác vụ hoặc chuỗi chỉ thiết lập cấu trúc hệ điều hành logic cho phép mã chạy. Trình lập lịch biểu của hệ điều hành thực hiện không ngay lập tức bắt đầu thực hiện nếu các lõi đang bận, luồng phải cạnh tranh với tất cả các luồng khác đang chạy trên máy. Một phiên Windows điển hình có một nghìn hoặc hơn. 64 lần mỗi giây, hạt nhân tạo ra một ngắt và lịch trình đánh giá lại những gì đang xảy ra để xem liệu một luồng khác có được lượt không. Bất kỳ chủ đề nào không chặn (chờ một số luồng khác để thực hiện công việc như đọc tệp hoặc gói mạng) đủ điều kiện để chạy và trình lên lịch chọn một luồng có ưu tiên cao nhất. Một số mã bổ sung trong các tinker scheduler với các giá trị ưu tiên để đảm bảo rằng tất cả các chủ đề có được một cơ hội.

Cơ hội là từ khóa ở đây. Lập lịch trình chủ đề là không xác định.

Lưu ý cách tôi chưa bao giờ nói gì về Chủ đề hoặc Tác vụ hoặc lớp ThreadPool. Họ không có khả năng làm nhiều thứ về cách hệ điều hành lên lịch cho các luồng. Tất cả những gì có thể là ngăn không cho một chuỗi chạy, công việc của bộ lập lịch nhóm luồng.

Các vấn đề ưu tiên, bạn có thể tinker với thuộc tính Thread.Priority hoặc Task.Priority để ảnh hưởng đến kết quả. Nhưng không có slamdunk, bộ lập lịch hệ điều hành liên tục điều chỉnh ưu tiên luồng từ mức ưu tiên cơ bản mà bạn đã đặt với thuộc tính đó. Bạn không thể ngăn chặn một chuỗi từ bao giờ bằng cách chạy một chuỗi khác có mức độ ưu tiên cao hơn chẳng hạn.

Mong rằng các chuỗi chạy theo thứ tự có thể dự đoán sẽ gây ra loại lỗi tồi tệ nhất, một lỗi chủng tộc. Thứ hai tồi tệ nhất là bế tắc. Chúng cực kỳ khó gỡ lỗi vì chúng phụ thuộc vào thời gian và các tài nguyên máy có sẵn và tải. Bạn chỉ có thể đảm bảo rằng bạn nhận được một thứ tự cụ thể bằng cách viết mã rõ ràng chăm sóc nó. Bạn làm gì bằng cách sử dụng từ khóa nguyên thủy như Mutex hoặc khóa. Đáng chú ý là khi bạn cố gắng thêm mã như vậy vào đoạn mã của bạn thì bạn sẽ kết thúc với một chương trình không còn đồng thời. Hay nói cách khác, bạn sẽ không còn sử dụng Task nữa. Một chuỗi hoặc tác vụ sẽ chỉ hữu ích nếu bạn có thể đủ khả năng để chạy hoặc không thể đoán trước được.

+0

+1 để nêu rõ "Lập lịch trình chủ đề không xác định". – Ramesh

7

Tại sao các tác vụ không chạy ngay sau StartNew?

Về MSDNStartNew() sẽ lịch một nhiệm vụ để thực hiện.

Calling StartNew là chức năng tương đương để tạo ra một tác sử dụng một trong những nhà xây dựng nó và sau đó gọi Start để sắp xếp nó cho thực hiện. Tuy nhiên, trừ khi việc tạo và lên lịch phải được tách riêng, StartNew là phương pháp được khuyến nghị cho cả sự đơn giản và hiệu suất .

Vì TPL sử dụng luồng ThreadPool - đôi khi nó phải thực hiện một số công việc để đặt trước và bắt đầu luồng ThreadPool cho một nhiệm vụ thực hiện cụ thể. Nếu bạn cần chạy một cách rõ ràng một luồng riêng biệt mà không có bất kỳ cơ chế trung gian nào như TaskScheduler của TPL - hãy tạo và bắt đầu một luồng thủ công và bạn sẽ không có những thứ gọn gàng như tiếp tục.

0

Lưu ý cách tôi chưa bao giờ nói gì về Chủ đề hoặc Công việc hoặc ThreadPool lớp học. Họ không có khả năng làm được gì nhiều về cách thức các luồng lịch trình của hệ điều hành. Tất cả những gì có thể là ngăn chặn một chuỗi chạy, công việc của bộ lập lịch nhóm luồng.

Chiến tranh ... Nhiệm vụ vs Chủ đề ... Tùy thuộc vào nhiệm vụ được yêu cầu. Đối với mẫu, chúng tôi cần tải 100 hình ảnh từ Internet với 100 tác vụ đồng thời (một nhiệm vụ cho một khách hàng) để tạo bản đồ cho 100 khách hàng (một ô cho một khách hàng). Và chúng tôi có giới hạn thời gian chung và thời gian tải của một số tác vụ tải có thể chồng chéo giới hạn thời gian chung. Các thử nghiệm đơn giản cho thấy rằng, đồng thời thực hiện 100 Chủ đề (chủ đề lớp) trong thời gian giới hạn, hiệu quả hơn nhiều so với thực hiện 100 nhiệm vụ (lớp nhiệm vụ). Kết quả tương tự cho 10 Chủ đề so với 10 Tác vụ. Tôi có nghĩa là nếu chúng ta cần nhiều hơn "một vài chậm chạp", nhưng mạnh mẽ, nhiệm vụ, tức là để làm nhiều công việc hơn trong các nhiệm vụ đồng thời, thì chúng ta nên sử dụng lớp Thread.

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