2009-10-01 31 views
142

Tôi có một câu hỏi phong cách về lựa chọn triển khai chuỗi nền tôi nên sử dụng trên một ứng dụng biểu mẫu cửa sổ. Hiện tại tôi có một số BackgroundWorker trên biểu mẫu có vòng lặp vô hạn (while(true)). Trong vòng lặp này, tôi sử dụng WaitHandle.WaitAny để giữ cho luồng tạm ẩn cho đến khi có điều gì đó quan tâm xảy ra. Một trong những sự kiện xử lý mà tôi chờ đợi là sự kiện "StopThread" để tôi có thể thoát ra khỏi vòng lặp. Sự kiện này được báo hiệu khi từ của tôi bị ghi đè Form.Dispose().BackgroundWorker vs background Chủ đề

Tôi đọc ở đâu đó rằng BackgroundWorker thực sự dành cho các thao tác mà bạn không muốn kết nối giao diện người dùng và có kết thúc hữu hạn - như tải xuống tệp hoặc xử lý chuỗi các mục. Trong trường hợp này, "kết thúc" không xác định và chỉ khi cửa sổ đóng. Do đó, tôi có thể sử dụng Chủ đề nền thay vì BackgroundWorker cho mục đích này?

Trả lời

72

Từ hiểu biết của tôi về câu hỏi của bạn, bạn đang sử dụng BackgroundWorker làm Chủ đề chuẩn.

Lý do tại sao BackgroundWorker được khuyến nghị cho những thứ bạn không muốn ràng buộc chuỗi giao diện người dùng là vì nó cho thấy một số sự kiện thú vị khi thực hiện phát triển Win Forms.

Sự kiện như RunWorkerCompleted để báo hiệu khi chuỗi đã hoàn thành những gì cần thiết và sự kiện ProgressChanged để cập nhật GUI về tiến trình chuỗi.

Vì vậy, nếu bạn không phải là việc sử dụng những thứ này, tôi không thấy bất kỳ tác hại nào trong việc sử dụng một Chuỗi chuẩn cho những gì bạn cần làm.

+0

một vấn đề khác mà tôi không chắc chắn là, giả sử tôi đang cố gắng hủy bỏ biểu mẫu có nhân viên nền đang chạy trên đó. Tôi báo hiệu shutdownevent (ManualResetEvent) và đôi khi sau đó DoWork sẽ thoát ra một cách duyên dáng.Tôi có nên để biểu mẫu tiếp tục và Vứt bỏ ngay cả khi DoWork có thể mất nhiều thời gian hơn để hoàn thành hoặc có cách nào đó (và tốt hơn) cho luồng.Cho nhân viên nền cho đến khi nó thực sự thoát và sau đó để Dispose của biểu mẫu tiếp tục? –

+0

Tôi nghĩ rằng BackgroundWorker.IsBusy là những gì bạn đang tìm kiếm ở đó. – ParmesanCodice

+1

Chỉ sử dụng 'CancelAsync' (và kiểm tra cho' CancellationPending' nếu chuỗi của bạn sẽ được bỏ phiếu trong khoảng thời gian ngắn, nếu bạn muốn có ngoại lệ được nêu ra, hãy sử dụng 'System.Threading.Thread.Abort()'. Trong chính chuỗi khối, chọn đúng kiểu cho tình huống –

2

Nếu nó không vỡ - sửa chữa nó cho đến khi nó là ... chỉ đùa thôi :)

Nhưng nghiêm túc BackgroundWorker có lẽ là rất giống với những gì bạn đã có, đã bạn bắt đầu với nó từ đầu có lẽ bạn đã có thể tiết kiệm thời gian - nhưng vào thời điểm này tôi không thấy cần thiết. Trừ khi một cái gì đó không hoạt động, hoặc bạn nghĩ rằng mã hiện tại của bạn là khó hiểu, sau đó tôi sẽ gắn bó với những gì bạn có.

1

Nhân viên nền là một lớp hoạt động trong một chuỗi riêng biệt, nhưng nó cung cấp chức năng bổ sung mà bạn không nhận được với một Chủ đề đơn giản (như xử lý báo cáo tiến trình nhiệm vụ).

Nếu bạn không cần các tính năng bổ sung do nhân viên nền cung cấp - và có vẻ như bạn không - thì một Chủ đề sẽ phù hợp hơn.

2

Sự khác biệt cơ bản là, như bạn đã nêu, tạo sự kiện GUI từ BackgroundWorker. Nếu luồng không cần cập nhật hiển thị hoặc tạo sự kiện cho luồng GUI chính, thì nó có thể là một luồng đơn giản.

10

Ngoài ra, bạn đang tạo ra một chuỗi threadpool cho toàn bộ thời gian của nhân viên nền, điều này có thể quan tâm vì chỉ có một số hữu hạn của chúng. Tôi sẽ nói rằng nếu bạn chỉ bao giờ tạo ra các chủ đề một lần cho ứng dụng của bạn (và không sử dụng bất kỳ tính năng của nhân viên nền) sau đó sử dụng một sợi, chứ không phải là một chủ đề backgroundworker/threadpool.

+1

Tôi nghĩ đây là một điểm tốt Vì vậy, thông điệp tôi lấy từ đây là sử dụng nhân viên nền nếu bạn cần một chuỗi nền "tạm thời" trong suốt vòng đời của Biểu mẫu , tuy nhiên nếu bạn cần một chuỗi nền cho toàn bộ vòng đời của biểu mẫu (có thể là phút, giờ, ngày ...) thì hãy sử dụng một Chủ đề thay vì BackgroundWorker để không lạm dụng mục đích của ThreadPool –

+0

Về: ". ..which có thể được quan tâm như là chỉ có một số hữu hạn của họ ", bạn có nghĩa là các ứng dụng khác trên hệ điều hành có thể cần chúng và chia sẻ từ cùng một 'hồ bơi'? –

327

Một số suy nghĩ của tôi ...

  1. Sử dụng BackgroundWorker nếu bạn có một nhiệm vụ duy nhất có thể chạy ở chế độ nền và cần phải tương tác với giao diện người dùng. Nhiệm vụ của dữ liệu và các cuộc gọi phương thức marshalling đến luồng UI được xử lý tự động thông qua mô hình dựa trên sự kiện của nó. Tránh BackgroundWorker nếu ...
    • lắp ráp của bạn không có hoặc không tương tác trực tiếp với giao diện người dùng,
    • bạn cần thread trở thành một chủ đề foreground, hoặc
    • bạn cần phải thao tác các ưu tiên thread.
  2. Sử dụng đề xuất ThreadPool khi có hiệu quả. ThreadPool giúp tránh chi phí liên quan đến việc tạo, bắt đầu và dừng luồng. Tránh sử dụng ThreadPool nếu ...
    • nhiệm vụ chạy cho các đời của ứng dụng của bạn,
    • bạn cần thread trở thành một chủ đề foreground,
    • bạn cần phải thao tác các ưu tiên chủ đề, hoặc
    • bạn cần chuỗi để có một bản sắc cố định (hủy bỏ, đình chỉ, khám phá).
  3. Sử dụng lớp Thread cho các tác vụ lâu dài và khi bạn yêu cầu các tính năng được cung cấp bởi mô hình luồng chính thức, chọn giữa chủ đề nền và nền, điều chỉnh ưu tiên luồng, kiểm soát hạt mịn .
+9

Background worker là trong ass System.dll không gian tên intly và System.ComponentModel. Không có sự phụ thuộc vào Winforms. – Kugel

+14

Đó là chính xác, nhưng 'BackgroundWorker' được thiết kế để báo cáo tiến trình luồng cho một bên quan tâm, thường liên quan đến giao diện người dùng. Các tài liệu MSDN cho lớp làm cho điều này rõ ràng rất nhiều. Nếu bạn chỉ cần một nhiệm vụ được thực hiện trong nền, thích sử dụng một chuỗi 'ThreadPool'. –

+5

Liên quan đến quan điểm của bạn về hội đồng 'System.Windows.Forms'; 'BackgroundWorker' cũng hữu ích cho các ứng dụng WPF và các ứng dụng đó có thể không có tham chiếu đến WinForms. – GiddyUpHorsey

11

Khá nhiều những gì Matt Davis cho biết, với những điểm bổ sung sau đây:

Đối với tôi khác biệt chính với BackgroundWorker là marshalling tự động của sự kiện hoàn thành thông qua SynchronizationContext. Trong ngữ cảnh giao diện người dùng, điều này có nghĩa là sự kiện đã hoàn thành sẽ kích hoạt trên luồng giao diện người dùng và do đó có thể được sử dụng để cập nhật giao diện người dùng. Đây là điểm khác biệt chính nếu bạn đang sử dụng BackgroundWorker trong ngữ cảnh giao diện người dùng.

Tác vụ được thực thi qua ThreadPool không thể dễ dàng hủy (điều này bao gồm ThreadPool. QueueUserWorkItem và các đại biểu thực thi không đồng bộ). Vì vậy, trong khi nó tránh được chi phí của spinup chủ đề, nếu bạn cần hủy bỏ hoặc sử dụng BackgroundWorker hoặc (nhiều khả năng bên ngoài giao diện người dùng) quay lên một sợi và giữ một tham chiếu đến nó để bạn có thể gọi Abort().

+1

Chỉ ... hy vọng ứng dụng được thiết kế về một phương pháp * clean * để ngăn chặn một tác vụ luồng (Abort thường không phải là nó) –

4

Tôi biết cách sử dụng các luồng trước khi tôi biết .NET, do đó, nó đã làm quen với khi tôi bắt đầu sử dụng BackgroundWorkers. Matt Davis đã tóm tắt sự khác biệt với sự xuất sắc tuyệt vời, nhưng tôi sẽ nói thêm rằng khó hiểu hơn chính xác mã đang làm gì và điều này có thể làm cho việc gỡ lỗi trở nên khó khăn hơn. Nó dễ dàng hơn để suy nghĩ về việc tạo và tắt các chủ đề, IMO, hơn là suy nghĩ về việc đưa ra công việc cho một nhóm các chủ đề.

tôi vẫn không thể bình luận bài viết của người khác, vì vậy tha thứ cho què nhất thời của tôi trong việc sử dụng một câu trả lời để giải quyết piers7

Không sử dụng Thread.Abort(); thay vào đó, hãy báo hiệu sự kiện và thiết kế chuỗi của bạn để kết thúc một cách duyên dáng khi được báo hiệu. Thread.Abort() đặt ra một ThreadAbortException tại một điểm tùy ý trong việc thực hiện của luồng, có thể làm tất cả các loại những thứ không vui như Màn hình mồ côi, trạng thái chia sẻ bị hỏng, và vân vân. http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx

8

Bạn biết đấy, đôi khi nó chỉ dễ dàng hơn để làm việc với một BackgroundWorker bất kể bạn đang sử dụng Windows Forms, WPF hay bất kỳ công nghệ nào. Phần gọn gàng về những kẻ này là bạn có được luồng mà không cần phải lo lắng quá nhiều về nơi bạn đang thread đang thực hiện, đó là tuyệt vời cho các nhiệm vụ đơn giản.

Trước khi sử dụng BackgroundWorker hãy cân nhắc đầu tiên nếu bạn muốn hủy một chuỗi (đóng ứng dụng, hủy người dùng) thì bạn cần phải quyết định xem chuỗi của bạn có nên kiểm tra hủy hay không.

BackgroundWorker.CancelAsync() sẽ thiết lập CancellationPending để true nhưng sẽ không làm bất cứ điều gì nhiều hơn, đó là sau đó các chủ đề trách nhiệm liên tục kiểm tra điều này, hãy nhớ rằng bạn cũng có thể kết thúc với một điều kiện chủng tộc trong cách tiếp cận này, nơi người dùng của bạn bị hủy bỏ, nhưng chủ đề đã hoàn thành trước khi thử nghiệm cho CancellationPending.

Thread.Abort() mặt khác sẽ ném một ngoại lệ trong quá trình thực hiện chuỗi thực thi hủy chuỗi đó, bạn phải cẩn thận về những gì có thể nguy hiểm nếu ngoại lệ này đột nhiên được nâng lên trong quá trình thực thi.

Threading cần xem xét rất cẩn thận không có vấn đề gì nhiệm vụ, đối với một số đọc thêm:

Parallel Programming in the .NET Framework Managed Threading Best Practices

0

tôi muốn chỉ ra một hành vi của lớp BackgroundWorker rằng đã không được đề cập được nêu ra. Bạn có thể tạo một Thread bình thường để chạy trong background bằng cách thiết lập thuộc tính Thread.IsBackground.

Chủ đề nền giống hệt với chủ đề tiền cảnh, ngoại trừ chủ đề nền không ngăn quá trình chấm dứt. [1]

Bạn có thể kiểm tra hành vi này bằng cách gọi phương thức sau trong hàm tạo của cửa sổ biểu mẫu.

void TestBackgroundThread() 
{ 
    var thread = new Thread((ThreadStart)delegate() 
    { 
     long count = 0; 
     while (true) 
     { 
      count++; 
      Debug.WriteLine("Thread loop count: " + count); 
     } 
    }); 

    // Choose one option: 
    thread.IsBackground = false; // <--- This will make the thread run in background 
    thread.IsBackground = true; // <--- This will delay program termination 

    thread.Start(); 
} 

Khi thuộc tính IsBackground được đặt thành true và bạn đóng cửa sổ, khi đó ứng dụng của bạn sẽ chấm dứt bình thường.

Nhưng khi thuộc tính IsBackground được đặt thành false (theo mặc định) và bạn đóng cửa sổ, khi đó cửa sổ sẽ biến mất nhưng quá trình vẫn tiếp tục chạy.

Lớp BackgroundWorker sử dụng Chủ đề chạy ẩn.

0

Điều gây khó hiểu với tôi là nhà thiết kế phòng thu trực quan chỉ cho phép bạn sử dụng BackgroundWorkers và Timers không thực sự làm việc với dự án dịch vụ.

Nó cung cấp cho bạn các điều khiển kéo và thả gọn gàng vào dịch vụ của bạn nhưng ... thậm chí không thử triển khai nó. Sẽ không hoạt động.

dịch vụ: Chỉ sử dụng System.Timers.Timer System.Windows.Forms.Timer sẽ không hoạt động mặc dù nó có sẵn trong hộp công cụ

dịch vụ: BackgroundWorkers sẽ không hoạt động khi nó đang chạy như một dịch vụ Sử dụng System.Threading.ThreadPools thay vào đó hoặc Async gọi

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