2009-09-05 29 views
16

Tôi tự hỏi sự khác biệt thực sự giữa việc gọi Thread.Sleep (1) và gọi SwitchToThread là gì (nếu chúng ta bỏ qua nó hiện không được tiếp xúc bởi BCL).SwitchToThread vs Sleep (1)

Joe Duffy đề cập trong his post rằng:

"!. Các kernel32 API SwitchToThread không thể hiện những vấn đề mà Sleep (0) và Sleep (1) làm" (liên quan đến hành vi của lịch trình)

Tại sao Sleep sẽ không hoạt động chính xác như SwitchToThread? Tại sao sự khác biệt này tồn tại, và cho điều gì tốt cho nó? (nếu có tất cả ..)

Trả lời

21

Có hai điểm khác biệt. Dữ liệu đầu tiên được đề cập trong tài liệu MSDN cho SwitchToThread:

Năng suất thực thi được giới hạn trong bộ xử lý của chuỗi cuộc gọi. Hệ điều hành sẽ không chuyển đổi thực thi sang bộ xử lý khác, ngay cả khi bộ xử lý đó không hoạt động hoặc đang chạy một chuỗi có mức độ ưu tiên thấp hơn.

Chế độ ngủ (0) cũng sẽ cho phép các luồng trên bộ xử lý khác chạy.

SwitchToThread cũng chỉ mang lại một ngữ cảnh lập lịch trình chuỗi đơn. Giấc ngủ, mặt khác, có nhiều điều kiện mà nó chờ đợi. Tài liệu cho số SleepEx giải thích chi tiết này:

* An I/O completion callback function is called 
* An asynchronous procedure call (APC) is queued to the thread. 
* The time-out interval elapses 

Điều này sẽ mang lại nhiều chủ đề.

Nói chung, chế độ Ngủ (0) sẽ có khả năng mang lại nhiều lần hơn và sẽ luôn mang lại hiệu suất cho hệ điều hành, ngay cả khi không có chủ đề nào khác chờ đợi. Đây là lý do tại sao thêm một Sleep (0) trong một vòng lặp sẽ sử dụng bộ vi xử lý từ 100% (trên mỗi lõi) đến gần 0% trong nhiều trường hợp. SwitchToThread sẽ không, trừ khi một thread khác đang đợi một lát thời gian.

+2

Với phân tích của bạn, sẽ không thực hiện 'if (! SwitchToThread()) Sleep (0);' là giải pháp tốt nhất sau đó? – wilx

0

SwitchToThread() là phiên bản "thông minh hơn" của Sleep (0). Nó không phải là tài liệu tốt, nhưng trong sự hiểu biết của tôi, nó hoạt động theo cách sau:

  1. khi có chủ đề khác trong tình trạng ready (tức là có nhiều chủ đề muốn chạy hơn so với bộ vi xử lý logic có sẵn) và những chủ đề là cùng mức ưu tiên hoặc cao hơn so với chuỗi gọi là SwitchToThread(), nó hoạt động theo cách tương tự như như Sleep (0) - tức là trích xuất bộ xử lý logic đến một trong các chủ đề này công tắc điện;
  2. khi có chủ đề ở trạng thái ready với mức ưu tiên thấp hơn, nó chỉ thoát, tức là luồng được gọi là SwitchToThread() tiếp tục thực thi mà không mất bất kỳ chi phí nào hoặc chuyển 3 lần sang 0 chuyển tiếp chế độ người dùng) - đây là trái với cách hoạt động của Sleep (0) luôn kiểm soát các chủ đề ưu tiên thấp nhất thậm chí;
  3. khi không có chủ đề ở trạng thái ready, SwitchToThread() cũng chỉ thoát như Sleep (0) - vì vậy nếu bạn thực hiện điều này trong vòng lặp, bạn chỉ nhận được 100% tải của bộ xử lý logic hiện tại, tức là đốt cháy sức mạnh.

Chế độ ngủ (1) giống như Sleep (0) nhưng chậm trễ 1 phần nghìn giây. Độ trễ 1 mili giây này giải phóng bộ xử lý logic và không ghi bất kỳ nguồn điện nào. SwitchToThread, ngược lại, không bao giờ gặp bất kỳ sự chậm trễ nào.

Vì vậy, tốt hơn nên so sánh SwitchToThread với Sleep (0), không phải với Sleep (1), vì Sleep (1) giống như Sleep (0) + delay của 1 mili giây.

Tôi đã mượn một số ý tưởng về vấn đề này từ "Hướng dẫn tham khảo tối ưu hóa kiến ​​trúc Intel 64 và IA-32" và "Hướng dẫn dành cho nhà phát triển phần mềm kiến ​​trúc Intel 64 và IA-32", có lợi cho một số hướng dẫn CPU pause có sẵn như là nội tại) trên SwitchToThread() hoặc Sleep (0) nếu chờ đợi của bạn là rất ngắn. Xin lưu ý rằng SwitchToThread() hoặc Sleep (0) gần như ngay lập tức, trong khi Sleep (1) kéo dài ít nhất một phần nghìn giây.

Sau đây cũng nên được xem xét:

  • Mỗi cuộc gọi Sleep() hoặc SwitchToThread() trải chi phí đắt đỏ của một chuyển ngữ cảnh, có thể là Trên 10000 chu kỳ.
  • Nó cũng chịu chi phí của vòng 3 để chuyển 0 chuyển tiếp, có thể là 1000+ chu kỳ.
  • SwitchToThread() hoặc Sleep (0) có thể không sử dụng nếu không có chủ đề ở trạng thái ready, nhưng Sleep (1) chờ ít nhất một mili giây bất kể có các luồng khác trong trạng thái 'sẵn sàng' hay không không phải.

Nếu vòng lặp chờ của bạn có xu hướng rất ngắn, trước tiên hãy cân nhắc thực hiện một số hướng dẫn CPU pause CPU trước. Bằng cách làm chậm “spin-chờ đợi” với một số pause hướng dẫn CPU trước SwitchToThread() hoặc một cuộc gọi Sleep(), các luồng đa lợi nhuận phần mềm:

  • Hiệu suất bằng cách hỗ trợ các công việc chờ đợi để có được nguồn lực hơn dễ dàng từ một chờ đợi bận rộn.
  • Tiết kiệm điện bằng cả hai cách sử dụng ít chi tiết hơn của đường ống trong khi quay.
  • Xoá bỏ phần lớn các lệnh được thực hiện không cần thiết gây ra bởi chi phí của lệnh gọi SwitchToThread() hoặc Sleep (0) hoặc Sleep (1).

Tuy nhiên, nếu bạn định gọi chế độ Ngủ (1) chạy ít nhất một mili giây rất dài về chu kỳ CPU, bạn mong đợi chu kỳ chờ đợi của bạn sẽ rất dài, vì vậy pause hướng dẫn sẽ vô ích trong trường hợp này. Khi vòng lặp chờ được mong đợi kéo dài, tốt hơn là nên mang đến hệ điều hành bằng cách gọi một trong các chức năng API đồng bộ hóa hệ điều hành, chẳng hạn như WaitForSingleObject trên hệ điều hành Windows, nhưng không phải là một SwitchToThread() hoặc Sleep (0). (0).) hoặc Ngủ (1), vì chúng rất lãng phí khi chờ đợi lâu. Hơn nữa, Sleep (1) là rất chậm và các chức năng đồng bộ hóa hệ điều hành như WaitForSingleObject hoặc EnterCriticalSection sẽ phản ứng nhanh hơn nhiều và chúng thân thiện với tài nguyên hơn.

Kết luận của tôi: tốt hơn là không nên sử dụng Sleep (0) hoặc Sleep (1) hoặc SwitchToThread(). Tránh các vòng “chờ đợi” bằng mọi giá. Sử dụng các hàm đồng bộ hóa cao cấp như WaitForMultipleObjects(), SetEvent(), v.v. - chúng là tốt nhất từ ​​các thuật ngữ về hiệu năng, hiệu quả và tiết kiệm điện năng.Mặc dù chúng cũng bị chuyển mạch bối cảnh đắt tiền và vòng 3 chuyển sang 0 chuyển tiếp, nhưng chi phí này không thường xuyên và hợp lý hơn so với những gì bạn đã trải qua trong các vòng “chờ đợi” với Sleep() hoặc SwitchToThread().

Trên bộ xử lý hỗ trợ Công nghệ HT, các vòng quay chờ có thể tiêu tốn một phần đáng kể băng thông thực thi của bộ xử lý. Một bộ xử lý logic thực thi vòng lặp chờ đợi có thể ảnh hưởng nghiêm trọng đến hiệu năng của bộ xử lý logic khác. Đó là lý do tại sao đôi khi vô hiệu hóa HT có thể cải thiện hiệu suất.

Việc bỏ phiếu liên tục cho một thiết bị hoặc tập tin hoặc nguồn dữ liệu khác để thay đổi trạng thái có thể khiến máy tính tiêu thụ nhiều điện năng hơn, gây căng thẳng trên bộ nhớ và bus hệ thống, và cung cấp lỗi trang không cần thiết. Windows để xem ứng dụng nào tạo ra hầu hết các lỗi trang trong khi ở chế độ chờ - đây là những ứng dụng không hiệu quả nhất vì chúng đang sử dụng "bỏ phiếu"). Giảm thiểu số lần bỏ phiếu bất cứ khi nào có thể và sử dụng cách viết ứng dụng theo sự kiện. Đây là phương pháp hay nhất mà tôi khuyên dùng. Ứng dụng của bạn phải theo nghĩa đen ngủ mọi lúc, chờ đợi nhiều sự kiện được thiết lập trước. Một ví dụ tốt về một ứng dụng hướng sự kiện là Nginx trong Linux. Lấy một ví dụ với việc bỏ phiếu cho các thay đổi nguồn điện. Nếu một hệ điều hành cung cấp dịch vụ thông báo (ngay cả thông báo WM_) cho các thay đổi trạng thái thiết bị khác nhau, chẳng hạn như chuyển nguồn điện từ AC sang pin, hãy sử dụng các dịch vụ thông báo này thay vì bỏ phiếu cho các thay đổi trạng thái thiết bị. Cách tiếp cận như vậy làm giảm chi phí cho mã để thăm dò tình trạng của nguồn điện, bởi vì mã có thể nhận được thông báo không đồng bộ khi thay đổi trạng thái xảy ra.

Trái ngược với những gì một số người đã viết, Chế độ ngủ (0) không giảm mức tiêu thụ CPU xuống gần bằng không. Nó giải phóng sự thực thi cho các luồng khác đang ở trạng thái 'sẵn sàng', nhưng nếu không có chủ đề như vậy, nó sẽ lãng phí hàng nghìn chu kỳ CPU và tiêu thụ 100% chu trình CPU của các luồng hiện tại, cũng như demonstrated by stackoverflow members - và tôi cũng chỉ cần kiểm tra lại điều này một lần nữa - vòng lặp Sleep (0) tiêu thụ 100% CPU của luồng hiện tại trên Windows 10 64-bit.

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