2009-07-17 22 views
12

Tôi đã có một ứng dụng kế thừa ở đây có một vài vòng 'tốn thời gian' bị loại bỏ do sự tương tác của người dùng khác nhau. Mã tốn thời gian định kỳ cập nhật một cái gì đó trên màn hình với thông tin tiến độ (thường là một nhãn) và sau đó, dường như thuyết phục việc làm mới hình ảnh xảy ra ở đó và sau đó, mã gọi Application.ProcessMessages (argh!). Bây giờ chúng ta đều biết loại rắc rối nào có thể giới thiệu với một ứng dụng GUI (từ thiện, đó là thời gian vô tội hơn) và chúng tôi nhận thấy chắc chắn là trứng, đôi khi chúng tôi thu hút người dùng đạt được không thể với chương trình bởi vì họ đang nhấp vào điều khiển trong khi chương trình là 'bận'.Nhận cửa sổ để làm mới (v.v.) mà không cần gọi Application.ProcessMessages?

Cách tốt nhất để làm mới định kỳ hình ảnh của biểu mẫu mà không cần thực hiện các sự kiện/tin nhắn khác, v.v ...?

Suy nghĩ của tôi là;
- vô hiệu hóa tất cả các điều khiển trước khi làm bất cứ điều gì tốn thời gian, và để lại các cuộc gọi của ... ProcessMessages' tại chỗ để 'lực lượng' làm mới, hoặc
- tìm một cách khác để làm mới một điều khiển kỳ

Tôi có thể làm điều cũ nhưng nó khiến tôi băn khoăn - liệu có giải pháp nào tốt hơn không?

mã mẫu từ cũ;

 
i:=0; 
while FJobToBeDone do 
begin 
    DoStepOfLoop; 
    inc(i); 
    if i mod 100 = 0 then 
    begin 
    UpdateLabelsEtc; 
    Application.ProcessMessages; 
    end; 
end; 

Tôi có thể nghe thấy tất cả các bạn ngất xỉu, ở phía sau. :-)

Trả lời

18

Nếu bạn gọi Cập nhật() trên các điều khiển sau khi bạn đã thay đổi thuộc tính, bạn sẽ buộc chúng vẽ lại. Một cách khác là gọi số Repaint() thay vì Làm mới(), ngụ ý gọi đến Cập nhật().

Bạn có thể cần phải gọi Update() trên điều khiển cha mẹ hoặc khung là tốt, nhưng điều này có thể cho phép bạn để loại bỏ các ProcessMessages() cuộc gọi hoàn toàn.

+0

giao ngay trên mghie , cảm ơn! Tôi đã bỏ điều này vào một vài địa điểm chính và thử nghiệm nhanh; các cập nhật màn hình chỉ diễn ra tốt nhưng các điều khiển không tạo ra các sự kiện 'mới' trong khi biểu mẫu đang bận. Nếu tôi kết hợp điều này cùng với một cách rõ ràng khóa xuống dưới hình thức khi công việc bắt đầu, tôi hy vọng rằng điều này sẽ ngăn chặn các sự kiện lừa đảo nhận được mặc dù. Cảm ơn nhiều! – robsoft

8

Giải pháp tôi sử dụng cho các bản cập nhật dài là thực hiện các phép tính trong một chuỗi riêng biệt. Bằng cách đó, chủ đề chính vẫn rất nhạy. Khi đã hoàn thành chuỗi, nó sẽ gửi một thông điệp Windows đến luồng chính, cho biết luồng chính có thể xử lý các kết quả.

Điều này có một số hạn chế nghiêm trọng khác. Trước hết, trong khi thread khác đang hoạt động, bạn sẽ phải vô hiệu hóa một vài điều khiển bởi vì chúng có thể khởi động lại thread. Hạn chế thứ hai là mã của bạn cần phải trở thành an toàn chỉ. Đây có thể là một thách thức thực sự đôi khi. Nếu bạn đang làm việc với mã cũ, rất có khả năng mã của bạn sẽ không an toàn với luồng. Cuối cùng, mã đa luồng khó gỡ lỗi hơn và nên được thực hiện bởi các nhà phát triển có kinh nghiệm.

Nhưng lợi thế lớn của đa luồng là ứng dụng của bạn vẫn đáp ứng và người dùng chỉ có thể tiếp tục thực hiện một số việc khác cho đến khi chuỗi xong. Về cơ bản, bạn đang dịch một phương thức đồng bộ thành một hàm không đồng bộ. Và luồng có thể kích hoạt một số thông báo cho biết các điều khiển nhất định để làm mới dữ liệu của riêng chúng, tức là dữ liệu sẽ được cập nhật ngay lập tức, ngay lập tức. (Và tại thời điểm bạn muốn chúng được cập nhật.)

Tôi đã tự sử dụng kỹ thuật này một vài lần, vì tôi nghĩ nó tốt hơn nhiều. (Nhưng cũng phức tạp hơn.)

+0

Cảm ơn, Hội thảo Alex - Tôi nghĩ rằng việc chuyển tiếp này là điều "đúng" để thử và làm, đặc biệt là nơi có thể có một thời gian dài không hoạt động. Tuy nhiên, trong trường hợp đặc biệt này, 'Cập nhật' của mghie dường như thả vào và thay thế toàn bộ cuộc gọi ProcessMessages. – robsoft

+0

Mặc dù Alex đưa ra một lời giải thích tốt, nó thiếu một con trỏ về cách bắt đầu: Delphi có một mẫu đơn vị chủ đề trong hộp thoại 'thêm mới', nhưng về bản chất tất cả đều tóm tắt để tạo ra một lớp dẫn xuất từ ​​TThread, ghi đè Thực thi phương thức. –

+0

Stijn đang cung cấp một cách tốt để sử dụng các chủ đề trong Delphi. Về cơ bản, lớp TThread kết thúc tốt đẹp quanh Windows API và cung cấp một lớp cơ sở nơi bạn có thể chia sẻ dữ liệu. Cá nhân, tôi thích sử dụng API thô thay vào đó, tạo ra một thủ tục thread riêng biệt gọi một phương thức cụ thể của lớp logic nghiệp vụ của tôi một lần nữa. (Hoặc một phương thức của hình thức chính/con của bạn.) Nhưng vì sự phức tạp, tôi thậm chí sẽ không dám sử dụng kỹ thuật này với mã di sản, trừ khi tôi sẽ viết lại nó từ đầu. (Mã kế thừa thường không an toàn cho luồng.) –

1

Kỹ thuật bạn đang tìm kiếm được gọi là luồng. Nó là một kỹ thuật khuếch tán trong lập trình. Mã nên được xử lý cẩn thận, bởi vì khó gỡ lỗi hơn. Cho dù bạn đi với luồng hay không, bạn chắc chắn nên vô hiệu hóa các điều khiển mà người dùng nên khônggây rối với (Tôi có nghĩa là các điều khiển có thể ảnh hưởng đến quá trình liên tục). Bạn nên xem xét sử dụng hành động để bật/tắt nút vv ...

+0

Cảm ơn Codervish này. Tôi phải thừa nhận trước đây tôi chưa từng sử dụng danh sách hành động nào nhưng chỉ xem xét kỹ hơn nó có vẻ như là một cách khá gọn gàng để kiểm soát toàn bộ sự kiện được kích hoạt/sự kiện. Chúc mừng! – robsoft

1

Thay vì vô hiệu hóa các điều khiển, chúng ta có một var boolean theo hình thức FBusy, sau đó chỉ cần kiểm tra điều này khi người dùng nhấn một nút, chúng tôi giới thiệu nó vì những lý do chính xác mà bạn đề cập, người dùng nhấp vào nút trong khi họ chờ đợi mã chạy dài để chạy (nó đáng sợ như thế nào quen thuộc mẫu mã của bạn).

Vì vậy, bạn kết thúc với một cái gì đó giống như

procedure OnClick(Sender:TObejct); 
begin 
    if (FBusy) then 
    begin 
     ShowMessage('Wait for it!!'); 
     Exit; 
    end 
    else FBusy := True; 
    try 
     //long running code 
    finally 
     FBusy := False; 
    end; 
end; 

nó bất lực để nhớ để rap mã chạy dài lên trong một thử-cuối cùng khối trong trường hợp thoát hoặc ngoại lệ, như bạn sẽ kết thúc với một hình thức rằng sẽ không làm việc. Chúng tôi đề nghị chúng tôi sử dụng các chủ đề nếu mã của nó không ảnh hưởng đến dữ liệu, nói báo cáo hoặc phân tích dữ liệu, nhưng một số điều này không phải là một tùy chọn, nói rằng chúng tôi đang cập nhật 20.000 hồ sơ sản phẩm, sau đó chúng tôi không không muốn bất cứ ai cố gắng bán hoặc thay đổi khôn ngoan các hồ sơ giữa chuyến bay, vì vậy chúng tôi phải chặn các ứng dụng cho đến khi nó được thực hiện.

+0

Cảm ơn vì điều này - cách tiếp cận này là cách tôi đã có xu hướng tự làm điều đó trong các ứng dụng khác. Tôi nghĩ rằng tôi sẽ cố gắng sử dụng một chuỗi cho mã chạy dài trong ứng dụng tiếp theo mà tôi viết, vì nó có vẻ giống như một thứ mà tôi phải cảm thấy thoải mái khi sử dụng. :-) – robsoft

3

Không gọi Application.Processmessages, Điều này chậm và có thể tạo ra đệ quy không giới hạn. Ví dụ, để cập nhật tất cả mọi thứ trong Panel1 mà không flick, chúng ta có thể sử dụng phương pháp này:

procedure TForm1.ForceRepaint; 
var 
    Cache: TBitmap; 
    DC: HDC; 
begin 
    Cache := TBitmap.Create; 
    Cache.SetSize(Panel1.Width, Panel1.Height); 
    Cache.Canvas.Lock; 
    DC := GetDC(Panel1.Handle); 
    try 
    Panel1.PaintTo(Cache.Canvas.Handle, 0, 0); 
    BitBlt(DC, 0, 0, Panel1.Width, Panel1.Height, Cache.Canvas.Handle, 0, 0, SRCCOPY); 
    finally 
    ReleaseDC(Panel1.Handle, DC); 
    Cache.Canvas.Unlock; 
    Cache.Free; 
    end; 
end; 

Đối với hiệu suất tốt hơn, bitmap bộ nhớ cache nên được tạo ra lúc đầu và miễn phí khi quá trình này đã hoàn thành

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