2009-07-07 33 views
6

Tôi đã làm việc trên cùng một dự án ngay từ Giáng sinh năm 2008. Tôi đã được yêu cầu mang nó từ một Ứng dụng Console (chỉ in các câu lệnh theo dõi), Ứng dụng Windows. Chắc chắn, đó là tốt. Điều duy nhất là có các phần của ứng dụng có thể mất vài phút đến gần một giờ để chạy. Tôi cần phải đa luồng để hiển thị trạng thái người dùng hoặc lỗi. Nhưng tôi không biết bắt đầu từ đâu.Đa luồng: cách cập nhật giao diện người dùng để biểu thị tiến trình

Tôi đã xây dựng một giao diện người dùng nhỏ trong WPF. Nó rất cơ bản, nhưng tôi muốn mở rộng nó như tôi cần. Ứng dụng hoạt động bằng cách chọn nguồn, chọn điểm đến và nhấp vào bắt đầu. Tôi muốn một hộp danh sách để cập nhật khi quá trình diễn ra. Nhiều trong cùng một cách SQL Server cài đặt, mỗi bước có một dấu kiểm màu xanh lá cây bằng tên của nó khi nó hoàn thành.

Làm thế nào để một newbie bắt đầu đa luồng? Tôi nên kiểm tra thư viện nào? Mọi lời khuyên sẽ được đánh giá cao.

p.s. Tôi hiện đang đọc về thư viện này, http://www.codeplex.com/smartthreadpool

@ Martin: Đây là làm thế nào ứng dụng của tôi được xây dựng:

  1. Động cơ: Chạy tất cả các thành phần chính trong trật tự được xác định trước
  2. Excel: Thư viện tôi đã viết để bọc COM để mở/đọc/đóng/lưu Sổ làm việc
  3. Thư viện: Thư viện hiểu các loại định dạng sổ làm việc khác nhau (5 tổng)
  4. Lớp học kinh doanh: Các lớp học tôi đã dịch để dịch dữ liệu Excel và chuẩn bị cho Access
  5. Db Thư viện: Một thư viện tôi đã viết trong đó sử dụng ADO.NET để đọc dữ liệu truy cập
  6. AppSettings: bạn sẽ có được ý tưởng
  7. Serialier: Lưu dữ liệu trong trường hợp ứng dụng sụp đổ

tôi sử dụng mọi thứ từ LINQ đến ADO.NET để lấy dữ liệu, chuyển đổi nó, và sau đó xuất nó.

yêu cầu chính của tôi là tôi muốn cập nhật giao diện người dùng của tôi cho thấy tiến trình

@Frank: Điều gì sẽ xảy ra nếu một cái gì đó trong Worker Bối cảnh ném một ngoại lệ (xử lý hay cách khác)? Đơn đăng ký của tôi nhận được thông báo như thế nào?

@Eric Lippert: Có, tôi đang điều tra điều đó ngay bây giờ. Trước khi tôi làm phức tạp mọi thứ.

Hãy cho tôi biết nếu bạn cần thêm thông tin. Hiện tại tôi đã chạy ứng dụng này từ một bài kiểm tra đơn vị, vì vậy tôi đoán callig nó là một ứng dụng giao diện điều khiển là không đúng sự thật. Tôi sử dụng Resharper để làm điều này. Tôi là người duy nhất ngay bây giờ sử dụng ứng dụng này, nhưng tôi muốn có một giao diện hấp dẫn hơn

+4

Trước khi bạn thêm một loạt phức tạp đa luồng, bạn có thể xem xét cách bạn viết ứng dụng của mình để đáp ứng với người dùng trong khi vẫn đang được tạo luồng đơn. Nó là hợp lý để thay đổi thuật toán của bạn để họ có thể làm một chút tính toán, trả lại thực tế là họ đã tiến bộ, để cập nhật giao diện người dùng, và sau đó tiếp tục tính toán nơi họ rời đi? –

+0

Nếu bạn đang đối phó với một API cơ sở dữ liệu sẽ rất khó khăn.Người dùng sẽ có mạng chậm, lỗi mạng, thời gian chờ tra cứu tên. DB sẽ bận vào các thời điểm vv Xử lý với I/O mạng thô có thể ở chế độ không chặn/không đồng bộ. Nhưng đi qua ADO.NET, đó không thực sự là một lựa chọn khả thi .. – nos

+0

@noselasd: Tôi chỉ sử dụng ADO.NET một chút, tôi không sử dụng nó làm cửa hàng phụ trợ của mình. – Chris

Trả lời

0

Cách tốt nhất cho tổng số người mới đến luồng có lẽ là threadpool. Chúng ta có thể cần biết thêm một chút về những phần này để đưa ra các khuyến nghị sâu hơn

EDIT ::
Vì bây giờ chúng ta có thêm một chút thông tin, tôi sẽ gắn bó với câu trả lời trước đây của tôi giống như bạn có vô số nhiệm vụ cần làm, cách tốt nhất để thực hiện các tác vụ là thêm chúng vào luồng và sau đó chỉ kiểm tra xem chúng có hoàn thành hay không, nếu các tác vụ cần được thực hiện theo thứ tự cụ thể thì bạn có thể chỉ cần thêm cái tiếp theo như phần trước đó kết thúc.Các threadpool thực sự là khá tốt cho loại điều và tôi thấy không có lý do không sử dụng nó trong trường hợp này

6

Tôi không nghĩ rằng bạn chỉ định phiên bản của CLR bạn đang sử dụng, nhưng bạn có thể kiểm tra " BackgroundWorker "kiểm soát. Đó là một cách đơn giản để triển khai nhiều luồng.

Phần tốt nhất, là nó là a part of the CLR 2.0 and up

Cập nhật để đáp ứng với bản cập nhật của bạn: Nếu bạn muốn để có thể cập nhật các tiến bộ trong giao diện người dùng - ví dụ như trong một thanh tiến trình - các nhân viên nền là hoàn hảo. Nó sử dụng một sự kiện mà tôi nghĩ được gọi là: ProgressChanged để báo cáo trạng thái. Đó là rất trang nhã. Ngoài ra, hãy nhớ rằng bạn có thể có nhiều trường hợp mà bạn cần và có thể thực hiện tất cả các cá thể cùng một lúc (nếu cần).

Để trả lời câu hỏi của bạn: Bạn có thể dễ dàng thiết lập dự án mẫu và kiểm tra câu hỏi của mình. Tôi đã tìm thấy những điều sau đây, here (dưới nhận xét, 2 đoạn từ thận trọng):

Nếu hoạt động đặt ra một ngoại lệ rằng mã của bạn không xử lý, các BackgroundWorker bắt ngoại lệ và chuyển nó vào trong RunWorkerTrình xử lý sự kiện đã hoàn thành, nơi nó được hiển thị là thuộc tính Lỗi của System.ComponentModel .. ::. RunWorkerCompletedEventArgs.

+0

Tôi nghĩ vậy. Nó chỉ là một lớp .net. –

+0

Cảm ơn, tôi sẽ đọc nó. – Chris

+0

Chỉ cần lưu ý, trước khi nhảy vào đa luồng thông qua công nhân nền - yu Phải biết về sự an toàn của luồng. Bạn phải biết rằng việc truy cập trực tiếp/cập nhật dữ liệu trong một luồng từ một luồng khác là nguy hiểm và không an toàn. Các liên kết khác ở đây giới thiệu cho bạn khóa và tương tự là cần thiết. – nos

0

This page là bản tóm tắt khá tốt về luồng. Bạn có thể không cần bất cứ điều gì rất phức tạp - nếu bạn chỉ mới bắt đầu nhiệm vụ và sau đó muốn biết khi nào nó đã hoàn thành, bạn chỉ cần một vài dòng mã để tạo ra một chủ đề mới và nhận được nó để chạy nhiệm vụ của bạn. Sau đó, chuỗi giao diện người dùng của bạn có thể bị vấp và kiểm tra định kỳ nếu tác vụ đã hoàn thành.

+0

@ Jason: Đó là một chút nhiều hơn thế. Tôi đang xử lý khoảng 5000+ Sách bài tập và tôi muốn biết sổ làm việc nào đang được xử lý, v.v., vì giao diện người dùng ở đó. – Chris

+0

Nếu nhu cầu của bạn đơn giản, bạn có thể sử dụng các chất bay hơi để truyền các giá trị giữa các luồng với nỗ lực tối thiểu. ví dụ. nếu bạn có một danh sách các sổ làm việc, bạn có thể chỉ ra tiến trình bằng cách sử dụng một int cho biết chỉ mục của sổ làm việc hiện tại. Bằng cách khai báo nó là "intatile int myIndex;" bạn không cần sử dụng khóa để truy cập an toàn giữa các chuỗi. Trong chuỗi giao diện người dùng, bạn có thể thêm Biểu mẫu.Timer gọi cho bạn (ví dụ) một vài lần trong một giây và bạn có thể cập nhật giao diện người dùng bằng cách chỉ đọc myIndex và báo cáo nó. Nếu bạn cần dữ liệu phức tạp hơn, sau đó đặt nó trong một lớp và sử dụng khóa xung quanh bất kỳ truy cập nào. –

+0

Lưu ý rằng dễ bay hơi chỉ có thể được sử dụng với một vài loại (như bool, int) –

0

Concurrent Programming on Windows là cuốn sách hay nhất về sự tồn tại của chủ đề. Viết bởi Joe Duffy, nổi tiếng Microsoft Guru đa luồng. Tất cả mọi thứ bạn cần biết và nhiều hơn nữa, từ cách Windows thread scheduler làm việc với .NET Parallels Extensions Library.

+0

Làm thế nào để một newbie bắt đầu đa luồng? Tôi nghi ngờ với cuốn sách này. – Nick

0

Hãy nhớ để tạo ra các đại biểu của bạn để cập nhật giao diện người dùng, do đó bạn không nhận được các vấn đề xuyên luồng và giao diện người dùng không dường như tạm ngừng/giam

Ngoài ra nếu bạn cần rất nhiều điểm ghi chú/điện/vv vv

tôi có thể đề nghị tất cả các bài giảng từ undergrad của tôi http://ist.psu.edu/courses/SP04/ist411/lectures.html

0

link Jason là một bài viết tốt. Những điều bạn cần lưu ý là giao diện người dùng chỉ có thể được cập nhật bởi luồng giao diện người dùng chính, bạn sẽ nhận được ngoại lệ luồng chéo nếu bạn cố gắng thực hiện nó trong luồng công nhân. Kiểm soát BackgroundWorker có thể giúp bạn ở đó với các sự kiện, nhưng bạn cũng nên biết về Control.Invoke (hoặc Control.Begin/EndInvoke). Điều này có thể được sử dụng để thực hiện các đại biểu trong bối cảnh của chuỗi giao diện người dùng.Ngoài ra, bạn nên đọc lên trên gotchas truy cập cùng một mã/biến từ các chủ đề khác nhau, một số trong những vấn đề này có thể dẫn đến các lỗi được liên tục và khó khăn để theo dõi xuống.

Một điểm cần lưu ý là từ khóa dễ bay hơi chỉ đảm bảo 'độ mới' của truy cập biến, ví dụ, nó đảm bảo rằng mỗi lần đọc và ghi của biến sẽ từ bộ nhớ chính chứ không phải từ bộ đệm hoặc bộ xử lý khác 'tính năng' của mô hình bộ nhớ. Nó không dừng các vấn đề như một chuỗi bị gián đoạn bởi một luồng khác trong quá trình đọc-cập nhật-ghi của nó (ví dụ: thay đổi giá trị biến). Điều này gây ra lỗi trong đó 2 luồng có giá trị khác nhau (hoặc giống nhau) cho biến và có thể dẫn đến những thứ như giá trị bị mất, 2 chuỗi có cùng giá trị cho biến khi chúng có giá trị khác nhau, v.v. Bạn nên sử dụng một khóa/màn hình (hoặc phương pháp đồng bộ hóa thread khác, chờ xử lý, interlockedincrement/decrement vv) để ngăn chặn các loại vấn đề, đảm bảo chỉ có một sợi có thể truy cập các biến. (Monitor cũng có lợi thế là nó thực hiện đọc/ghi dễ bay hơi)

Và như một người khác đã lưu ý, bạn cũng nên cố gắng tránh chặn chuỗi giao diện người dùng của mình trong khi chờ đợi các chủ đề nền hoàn thành, nếu không giao diện người dùng của bạn sẽ không phản hồi . Bạn có thể thực hiện việc này bằng cách yêu cầu chuỗi công việc của bạn nâng cao các sự kiện mà UI của bạn đăng ký cho biết các tiến trình hoặc hoàn thành.

Matt

0

Typemock có một công cụ mới có tên gọi Racer giúp với vấn đề đa luồng. Đó là một chút tiên tiến nhưng bạn có thể nhận được trợ giúp trên diễn đàn của họ và trong diễn đàn trực tuyến khác (một trong đó kỳ lạ đến với tâm là stackoverflow :-))

0

Tôi là một newbie để đa luồng là tốt, nhưng tôi đồng ý với Frank rằng một nhân viên nền có lẽ là lựa chọn tốt nhất của bạn. Nó hoạt động thông qua đăng ký sự kiện. Đây là những điều cơ bản về cách bạn sử dụng nó.

  • Đầu tiên tạo một nền công nhân mới
  • phương đang theo dõi trong mã của bạn để người lao động nền sự kiện lớn:
    • DoWork: Đây nên chứa bất kỳ mã mà phải mất một thời gian dài để xử lý
    • ProgressChanged : Điều này được ghen tị bất cứ khi nào bạn gọi ReportProgress() từ bên trong phương thức đã đăng ký với DoWork
    • RunWorkerCompleted: Envoked khi phương thức DoWork đã hoàn thành

Khi bạn đã sẵn sàng để chạy thời gian của bạn tiêu thụ quá trình bạn gọi phương thức RunAsync() của người lao động nền. Điều này bắt đầu phương thức DoWork trên một luồng riêng biệt, sau đó có thể báo cáo tiến trình của nó trở lại thông qua sự kiện ProgressChanged. Khi nó hoàn thành RunWorkerComplete sẽ được gợi lên.

Phương thức sự kiện DoWork cũng có thể kiểm tra xem người dùng có yêu cầu hủy bỏ quy trình này hay không (CanceLAsync() được gọi)) bằng cách kiểm tra giá trị của thuộc tính CancelPending.

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