2009-09-29 39 views
6

Tôi tìm thấy điều này trên trang web của Tiến sĩ Dobbs ngày hôm nay tại http://www.ddj.com/hpc-high-performance-computing/220300055?pgno=3 Đó là một gợi ý tốt đẹp liên quan đến việc cấy chỉ. Cách tốt nhất để đạt được điều này với TThread ở Delphi là gì? Cảm ơn BrianLàm thế nào để chương trình số lượng các chủ đề của bạn trong Delphi

=== Từ Dr Dobbs ==============

Make đa luồng cấu hình! Số lượng các chủ đề được sử dụng trong một chương trình nên luôn luôn được cấu hình từ 0 (không có chủ đề bổ sung ở tất cả) đến một số tùy ý. Điều này không chỉ cho phép một tùy chỉnh cho hiệu suất tối ưu, nhưng nó cũng chứng minh là một công cụ gỡ lỗi tốt và đôi khi một phao cứu sinh khi các điều kiện chủng tộc chưa biết xảy ra trên các hệ thống khách hàng. Tôi nhớ nhiều hơn một tình huống mà khách hàng đã có thể khắc phục lỗi nghiêm trọng bằng cách tắt đa luồng. Điều này tất nhiên không chỉ áp dụng cho đa luồng tập tin I/O.

Hãy xem xét các giả sau đây:

int CMyThreadManger::AddThread(CThreadObj theTask) 
{ 
    if(mUsedThreadCount >= gConfiguration.MaxThreadCount()) 
     return theTask.Execute(); // execute task in main thread 
    // add task to thread pool and start the thread 
    ... 
} 

một cơ chế như vậy không phải là rất phức tạp (mặc dù làm việc nhiều hơn một chút có thể sẽ cần thiết hơn thể hiện ở đây), nhưng đôi khi nó rất hiệu quả. Nó cũng có thể được sử dụng với các thư viện luồng dựng sẵn như OpenMP hoặc các khối xây dựng luồng của Intel. Xem xét các số đo được hiển thị ở đây, một ý tưởng hay là bao gồm nhiều hơn một chuỗi chỉ số cấu hình (ví dụ, một cho các tệp I/O và một cho các nhiệm vụ CPU lõi). Giá trị mặc định có thể là 0 đối với số lượng tệp I/O và < được tìm thấy > cho các tác vụ CPU. Nhưng tất cả đa luồng nên có thể tháo rời. Một cách tiếp cận phức tạp hơn thậm chí có thể bao gồm một số mã để kiểm tra hiệu năng đa luồng và thiết lập số lượng các luồng được sử dụng tự động, thậm chí có thể riêng cho các tác vụ khác nhau.

===================

Trả lời

0

tôi thường chỉ có một lớp kế thừa từ TThread, một trong đó có 'mục nhân' từ một hàng đợi hoặc chồng, và khiến họ tạm ngưng khi không còn mặt hàng nào nữa. Chương trình chính sau đó có thể quyết định có bao nhiêu trường hợp của chuỗi này để khởi tạo và bắt đầu. (sử dụng giá trị cấu hình này).

'Hàng đợi công nhân' này cũng phải đủ thông minh để tiếp tục chuỗi bị treo hoặc tạo chuỗi mới khi được yêu cầu (và khi giới hạn cho phép), khi một mục công nhân được xếp hàng hoặc một chuỗi đã xử lý xong mục công nhân .

+5

Không cần thiết phải tạm dừng và tiếp tục chuỗi. Để mỗi thread đợi một sự kiện hoặc semaphore, hoặc sử dụng 'WaitMessage()' với một vòng lặp thông báo chuỗi (có thể cần thiết cho OLE). Có rất nhiều cuộc thảo luận về chủ đề ở đây trên SO theo [delphi], đọc nó, nhưng tốt nhất là bỏ qua các đối số của những người nghĩ rằng họ biết rõ hơn Embarcadero và các nhà phát triển Microsoft, và cố gắng nói với bạn bằng cách sử dụng 'Suspend()' và 'Resume()' sẽ ổn. – mghie

5

Tôi sẽ tạo một lớp trừu tượng TTask. Lớp này có nghĩa là thực hiện nhiệm vụ. Với phương thức Execute:

type 

    TTask = abstract class 
    protected 
    procedure DoExecute; virtual; abstract; 
    public 
    procedure Execute; 
    end; 

    TTaskThread = class (TThread) 
    private 
    FTask : TTask; 
    public 
    constructor Create(const ATask: TTask); 
    // Assigns FTask and enables thread, free on terminate. 

    procedure Execute; override; // Calls FTask.Execute. 
end; 

Phương thức Thực thi kiểm tra số lượng chủ đề. Nếu không đạt đến mức tối đa, nó sẽ bắt đầu một luồng bằng cách sử dụng TTaskThread để gọi DoExecute và thực hiện nhiệm vụ đó trong một luồng. Nếu đạt đến mức tối đa, DoExecute được gọi trực tiếp.

4

The answer by Gamecat là tốt như xa như lớp nhiệm vụ trừu tượng là có liên quan, nhưng tôi nghĩ gọi DoExecute() cho một nhiệm vụ trong chuỗi gọi (như bản thân bài viết không quá) là một ý tưởng tồi. Tôi sẽ luôn luôn xếp hàng các nhiệm vụ được thực hiện bởi các chủ đề nền, trừ khi luồng đã bị tắt hoàn toàn, và đây là lý do tại sao.

xem xét như sau (giả tạo) trường hợp, nơi bạn cần phải thực hiện ba thủ tục CPU-bound độc lập:

Procedure1_WhichTakes200ms; 
Procedure2_WhichTakes400ms; 
Procedure3_WhichTakes200ms; 

Đối với việc sử dụng tốt hơn các hệ thống lõi kép của bạn, bạn muốn thực hiện chúng trong hai chủ đề. Bạn sẽ giới hạn số lượng các chủ đề nền cho một, vì vậy với các chủ đề chính bạn có nhiều chủ đề như lõi.

Bây giờ quy trình đầu tiên sẽ được thực thi trong chuỗi công nhân và nó sẽ kết thúc sau 200 mili giây. Thủ tục thứ hai sẽ bắt đầu ngay lập tức và được thực thi trong luồng chính, vì chuỗi công nhân được cấu hình đơn đã bị chiếm đóng và nó sẽ kết thúc sau 400 mili giây. Sau đó, thủ tục cuối cùng sẽ được thực hiện trong chuỗi công nhân, đã được ngủ trong 200 mili giây và sẽ kết thúc sau 200 mili giây. Tổng thời gian thực hiện 600 mili giây, và 2/3 thời gian đó chỉ có một trong hai chủ đề thực sự làm việc có ý nghĩa.

Bạn có thể sắp xếp lại các thủ tục (nhiệm vụ), nhưng trong cuộc sống thực, có lẽ không thể biết trước mỗi công việc sẽ mất bao lâu.

Bây giờ hãy xem xét cách phổ biến để sử dụng một nhóm luồng. Theo cấu hình, bạn sẽ giới hạn số lượng các chủ đề trong nhóm đến 2 (số lõi), chỉ sử dụng chuỗi chính để lên lịch các luồng vào trong nhóm và sau đó đợi tất cả các tác vụ hoàn thành. Với chuỗi thứ tự trên của chuỗi nhiệm vụ xếp hàng 1 sẽ thực hiện nhiệm vụ đầu tiên, luồng thứ hai sẽ thực hiện nhiệm vụ thứ hai. Sau 200 mili giây, nhiệm vụ đầu tiên sẽ hoàn thành và chuỗi công nhân đầu tiên sẽ thực hiện nhiệm vụ thứ ba từ nhóm, sau đó nó sẽ trống. Sau 400 mili giây, tác vụ thứ hai và thứ ba sẽ hoàn thành và chủ đề chính sẽ được bỏ chặn. Tổng thời gian thực hiện 400 mili giây, với 100% tải trên cả hai lõi trong thời gian đó.

Ít nhất là đối với các chuỗi bị ràng buộc CPU, điều quan trọng là luôn có công việc xếp hàng đợi cho trình lên lịch OS. Gọi số DoExecute() trong chủ đề chính cản trở điều đó và không nên thực hiện.

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