2013-06-07 24 views
14

Thứ nhất, tôi không hỏi cùng một câu hỏi như C# - Alternative to Thread.Sleep? hoặc Alternative to Thread.Sleep in C#?. Tôi không nghĩ rằng tôi đang sử dụng nó không chính xác và cần một sự thay thế chính hãng cho các tình huống cụ thể.Cách khác để sử dụng Thread.Sleep để chờ

Trong một hoạt động phân tích mã tôi thấy một sự vi phạm bất ngờ sắp tới:

Sử dụng Thread.Sleep() là một dấu hiệu của thiết kế sai lầm.

Vi phạm này dẫn đến Peter Richie's article về lý do chính xác điều này cấu thành thiết kế kém.

Chúng ta đều biết tạo luồng là tốn kém và chặn trong chuỗi có nghĩa là tranh chấp trên hồ bơi. Chúng tôi cũng biết rằng mỗi luồng sẽ phân bổ một meg bộ nhớ vì vậy nó sẽ có tuổi thọ ngắn, chặn trên giao diện người dùng là điều ác, sử dụng thời gian ngủ là không đáng tin cậy, v.v. v.v. Điều này dẫn tôi đến điểm của tôi, nếu bạn thực sự cần thực hiện một giấc ngủ, những gì bạn nên sử dụng nếu không Thread.Sleep?

Peter tiếp tục đề cập đến rằng giấc ngủ bằng 0 là cách sử dụng chính xác nhất của Thread.Sleep có hiệu quả từ bỏ thời gian của chuỗi và cho phép các luồng khác xử lý. Và thậm chí đáng sợ hơn nữa là đây chỉ là một hạn chế về các luồng không được quản lý và nếu được tái triển khai trong CLR sẽ tạo ra các tác dụng phụ của việc sử dụng Thread.Sleep trong các ứng dụng của bạn. Tất cả các điểm về sử dụng phổ biến xấu, trên thực tế, ví dụ tốt về sử dụng xấu.

Tôi có các tình huống sau đây trong mã sản xuất có sử dụng Thread.Sleep khá thành công:

  • Chờ một khóa tập tin để xin trả lại bởi hệ điều hành (vấn đề khóa tập tin bắt, chờ đợi trong giây lát , thử lại, bỏ cuộc sau một lúc).
  • Giết một quy trình và chờ nó không hiển thị trong danh sách quá trình (hãy xóa nó, kiểm tra nó không chạy, đợi giây thứ hai, kiểm tra nó vẫn chưa chạy, buộc đóng).
  • Đang chờ bộ đệm sao chép tuôn ra (kiểm tra kích thước tệp, hãy thử truy cập bộ đệm, chờ, kiểm tra xem kích thước đã thay đổi) chưa.

Không sử dụng Thread.Sleep trong các tình huống như thế này, tôi có những tùy chọn nào khác? Các vòng chặt chẽ có xu hướng làm mọi việc tồi tệ hơn và tôi không tin điều này làm cho nó sử dụng "lỗi thiết kế" đặc biệt là vì không có gì trên giao diện người dùng và chỉ trong các chủ đề nền. Nó chỉ là bản chất của phần mềm để chờ đợi những thứ khác trong môi trường đa luồng với các yếu tố bên ngoài ảnh hưởng đến mã của bạn, đôi khi bạn cần phải chờ ...

+1

nào bạn muốn nói, Thread.Sleep gì là không thể tránh khỏi? sau đó sử dụng nó. Nhưng tôi không bao giờ muốn sử dụng nó. – I4V

+0

Trong khi bài viết của Peter có nhiều điểm tốt, nó không lý do thực tế là nó chỉ đơn giản là sai. –

Trả lời

11

Các loại và loại có nguồn gốc cung cấp cơ chế theo hướng sự kiện cho chờ đợi mối quan hệ đó vào hệ điều hành. Ví dụ: khi bạn có số Task<T> task và bạn đợi kết quả bằng cách truy cập task.Result, triển khai nội bộ không bỏ phiếu với các cuộc gọi Thread.Sleep ở giữa. Nó đang sử dụng loại WaitHandle-được tạo để chờ và đồng bộ hóa.

Đôi khi một cách tiếp cận bỏ phiếu dựa trên là cần thiết, như trong một số ví dụ bạn đưa ra trong danh sách đạn của bạn, nhưng thường bạn thể sử dụng một cách tiếp cận hướng sự kiện để thay thế.Không phải là Thread.Sleepluôn luôn không sao - chỉ là rất thường bị lạm dụng.

Nó chỉ là bản chất của phần mềm để chờ đợi cho những thứ khác trong một môi trường đa luồng với các yếu tố bên ngoài ảnh hưởng đến mã của bạn, đôi khi bạn cần phải chờ đợi ...

Để chờ là tốt. Để hãy đợi bằng cách bỏ phiếu thường không phải (*). Nếu có bất kỳ cách nào bạn có thể sử dụng chờ đợi theo sự kiện, bạn thường nên cố gắng sử dụng điều đó.

Tôi không có cảm giác rất tốt về chính xác những gì bạn đang yêu cầu, vì vậy tôi sẽ không giải thích ngoài việc này. Nếu bạn để lại một bình luận tôi có thể mở rộng câu trả lời của tôi.


(*) Lý do lý thuyết chờ đợi với bỏ phiếu là xấu như sau:

Giả sử tôi có mã trông như thế này:

//START 
Begin(); 
while (!Done()) 
    Thread.Sleep(D); 
//STOP 

Begin() bắt đầu một số hoạt động . Done() trả lại true có nghĩa là thao tác đã kết thúc. Giả sử điều này sẽ xảy ra sau khoảng thời gian xấp xỉ T. Sau đó:

  • Sợi chỉ tỉnh dậy và kiểm tra các điều kiện (gọi Done()) T/D lần
  • Thời gian từ START để STOP bao gồm một dự kiến ​​D/2 thuần túy vì Thread.Sleep

giá trị gì về D bạn nên chọn? Khi bạn tăng D, dạng thời gian dự kiến ​​là START đến STOP tăng tuyến tính. Khi bạn giảm D, số lần lặp (tăng) trên số lượng tăng là 1/D. Cả hai đều là xấu, và việc tìm kiếm quyền D là có vấn đề.

Bây giờ so sánh này đến một hướng sự kiện chờ đợi:

//START 
Begin(); 
WaitDone(); 
//STOP 

nói lý thuyết, chừng nào WaitDone() bằng cách nào đó kỳ diệu đợi cho đến khi hoạt động đã kết thúc nhưng không còn, cả trong những vấn đề được xác định trong chờ đợi với bỏ phiếu trường hợp đã biến mất: chuỗi này chờ chính xác số lượng thời gian phù hợp - không nhiều, không kém!

Để nhắc lại điểm tôi đã bắt đầu với: trong .NET, loại lớp học và loại có nguồn gốc WaitHandle là những gì tạo điều kiện thuận lợi cho cách tiếp cận này.

+1

Có lẽ vì downvoter là clueless. 1 cho câu trả lời tuyệt vời, đặc biệt. cho 'không Thread.Sleep luôn luôn là xấu - nó chỉ là nó thường rất lạm dụng'. –

+0

Tôi đang hướng tới đây như là câu trả lời tốt nhất đơn giản bởi vì nó bào chữa cho tôi khi sử dụng Thread.Sleep trong bối cảnh cụ thể của tôi :) và cũng làm nổi bật với mọi người rằng WaitHandle là một cách tối ưu để chờ các chủ đề kết thúc. Tôi sử dụng ResetEvents thường xuyên vì tôi không thể chờ đợi một tín hiệu và thời gian chờ khá dễ dàng, tuy nhiên, trong những trường hợp này tôi đã không thể thoát khỏi sự quyến rũ của chỉ bằng cách sử dụng một Thread.Sleep. Lấy mã này cho ví dụ: http://stackoverflow.com/questions/16774412/relog-cant-open-a-binary-log-file-if-executed-from-c-sharp/16775074#16775074, tôi cần một làm chậm không giết hệ điều hành – BrutalDev

+0

@BrutalDev Nếu điều bạn đang giao tiếp không hỗ trợ các hoạt động không đồng bộ theo hướng, cách duy nhất để thực hiện một số hoạt động nhất định là thông qua bỏ phiếu. Ví dụ, trong Windows 7 và trước đó, các API của Windows chỉ đơn giản là không cho phép bạn * đợi * để khóa tập tin; nhưng trong Windows 8, các API WinRT đã được thiết kế lại để hỗ trợ các hoạt động không đồng bộ theo định hướng sự kiện. –

2

Vâng, bạn đã nói hầu hết. Trích dẫn "Chúng ta đều biết tạo thread là tốn kém và ngăn chặn trong chủ đề có nghĩa là ganh đua trên hồ bơi", vì vậy bạn thậm chí hiểu những gì bằng cách sử dụng một nhóm các chủ đề là về.

Bạn cũng hiểu rằng việc chặn chuỗi giao diện người dùng không tốt.

Nhìn vào hồ sơ của mô hình chủ đề một lần nữa: bạn có một nhóm các chủ đề, có thể một cho mỗi bộ xử lý, và sau đó vượt qua các nhiệm vụ cho họ. Điều gì sẽ làm cho một trong những chủ đề để chặn? Nếu nó không có công việc để thực hiện bây giờ, sau đó nó chỉ đơn giản là nên tiến hành một nhiệm vụ khác nhau.

Vì vậy, chuyển trực tiếp đến câu hỏi của bạn "Điều này dẫn tôi đến điểm của tôi, nếu bạn thực sự cần ngủ, bạn nên sử dụng cái gì nếu không Thread.Sleep?", Trong một chương trình được thiết kế hiện đại, bạn sẽ không bao giờ cần phải làm điều đó, bạn chỉ đơn giản là sắp xếp một nhiệm vụ cho sau này.

Bạn nên nghĩ về các chủ đề trong một hồ bơi, giống như các bộ xử lý trong một hệ thống, là tài nguyên, sẽ được phát hành cho những người khác khi không cần thiết.

Đi đến ví dụ của bạn, bạn có chút tham gia vào mô hình lập trình bắt buộc.

  • Bạn không cần phải chờ quá trình biến mất ... Tôi không tưởng tượng tại sao bạn cần điều này, nhưng nếu phải đợi, đó là vì bạn có công việc để thực hiện sau một thời gian, "tiếp tục "của chức năng của bạn. Bạn nên thiết lập một bộ đếm thời gian cho "tiếp tục" này.
  • Ví dụ tệp phải có các cơ chế khác cho nó, nếu chúng không có .... đó sẽ là thiết kế hệ điều hành tốt. Ví dụ, cách an toàn duy nhất để chờ các bộ đệm để tuôn ra sẽ là một nguyên thủy của hệ điều hành, giống như fsync.
  • Nếu có ai đó ghi vào một tệp và sau đó đọc khác từ tệp, thì cần có cơ chế đồng bộ hóa, không phải chờ đợi theo thời gian (trừ khi tệp đó chỉ nối thêm, trong trường hợp đó tệp đó là cơ chế đồng bộ hóa) .

Chờ cơ chế đồng bộ hóa không phải là "xấu".

+0

Cảm ơn bạn, những điểm tốt. Để xây dựng trên các điểm: 1. Chờ đợi cho các ứng dụng để chết chỉ là cho độ tin cậy, do đó, kiểm tra sẽ đảm bảo rằng nó không gần duyên dáng nếu không nó sẽ buộc nó. 2. Vấn đề ở đây là không có gì từ hệ điều hành (ít nhất là trong mã được quản lý) cho bạn biết khi một tập tin ghi hoàn tất. Nhìn vào điều này cho một ví dụ về cách nó gần như không thể tránh khỏi để làm chậm và chờ đợi cho hệ điều hành: http://stackoverflow.com/questions/16774412/relog-cant-open-a-binary-log-file-if-executed- from-c-sharp/16775074 # 16775074 3. Chống virus là một ví dụ có thể khóa bất ngờ. – BrutalDev

-1

Trong một trong những dự án của tôi, tôi sử dụng 2 chủ đề và tôi đã có vấn đề với giao diện người dùng đóng băng thnx để Thread.Sleep .... này cố định vấn đề của tôi:

public static void Sleeping(int miliseconds) 
    { 
     var task = Sleep(miliseconds); 
     task.Wait(); 
    } 

    public static async Task Sleep(int miliseconds) 
    { 
     await Task.Delay(miliseconds); 
    } 
+0

Task.Delay (1000) .Wait() là một lựa chọn tốt cho Thread.Sleep (1000). Xem https://stackoverflow.com/a/20084603/3115559 để có giải thích tốt. – Darky711

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