Parallel Programing
Lập trình song song
Lập trình song song
Đa luồng (multi-threading) và parallel programing (lập trình song song) là hai thành phần không thể thiếu của một ứng dụng hiệu suất cao. Thông thường, ứng dụng sẽ chạy trên một thread chính và trên một lõi CPU. Với sự phát triển của phần cứng cho phép sử dụng đồng thời nhiều lõi CPU, việc cần phải phân bổ hợp lí công việc thành nhiều thread nhỏ giúp nâng cao hiệu năng làm việc của chương trình.
Delphi cung cấp một cách dễ dàng để viết mã song song. Những phiên bản đầu tiên, Delphi sử dụng một lớp TThread được override phương thức Execute để thực hiện. Tuy nhiên nhược điểm của nó là khó viết mã và dài dòng.
Bây giờ, việc viết mã song song trong Delphi đã dễ dàng hơn với Parallel Loop và ITask (unit System.Threading).
Delphi cung cấp một cách dễ dàng để chạy một vòng lặp song song, sử dụng parallel loop. Lưu ý các lần chạy phải không liên quan dữ liệu với nhau để tránh xung đột (không sử dụng chung một biến nào đó).
uses
System.Threading;
procedure DoSomeThing(i: Cardinal);
begin
Sleep(i);
end;
procedure DoNormalLoop;
var i: Cardinal;
begin
for i := 0 to 199 do
DoSomeThing(10);
end;
procedure DoParallelLoop;
var i: Cardinal;
begin
TParallel.For(0, 199, procedure (i: Cardinal)
begin
DoSomeThing(i);
end);
end;
begin
DoLoopNormal;
DoParallelLoop;
end.
Trên đây là một chương trình đơn giản gồm một thủ tục DoSomeThing để làm một việc gì đó (ở trên mình để nó Sleep một khoảng i miligiây, các bạn có thể thay thế bằng một số câu lệnh khác).
Và hai thủ tục: DoNormalLoop và DoParallelLoop. Về cơ bản cả hai thủ tục này đều thực hiện lặp 200 lần (từ 0 đến 199) để thực hiện DoSomeThing.
Và hãy xem xét tốc độ của chúng:
Dễ thấy, với máy tính có càng nhiều lõi CPU thì tốc độ thực hiện vòng lặp sẽ nhanh hơn gấp nhiều lần.
Về cơ bản khi parallel loop hoạt động nó sẽ sử dụng hết tối đa số lõi CPU hiện có, làm chương trình hoạt động nhanh hết mức có thể. Nó cũng tạo ra số thread phụ bằng đúng số lõi CPU và phân chia công việc cho các thread này.
Như ví dụ trên lặp 200 lần, nếu máy tính có 4 core CPU thì Parallel loop sẽ chia công việc ra làm 4, chia làm 4 threads và mỗi thread thực hiện lặp 50 lần.
Lưu ý: Khi sử dụng Parallel loop, thủ tục cần thực hiện là DoSomeThing cần có một tham số, không được để trống.
Nói sơ qua một chút về lập trình GUI. Như các bạn biết Delphi hỗ trợ mạnh việc lập trình giao diện dễ dàng. Chương trình có giao diện (tạm gọi là chương trình GUI) sử dụng duy nhất một thread để vừa thực hiện việc cập nhật giao diện (main thread) và xử lí dữ liệu chung.
Nhược điểm của việc này có thể kể đến như truy xuất cơ sở dữ liệu và xuất thông tin phù hợp sau khi click vào một nút. Đối với cơ sở dữ liệu nhỏ thì thời gian truy xuất nhanh nên ứng dụng vẫn hoạt động bình thường. Nhưng đối với các cơ sở dữ liệu lớn thì thời gian truy xuất lâu, sẽ dẫn đến tình trạng đóng băng ứng dụng (not responding).
Để khắc phục điều này, chúng ta sẽ sử dụng ITask, tách riêng các luồng với nhau:
Cách này sẽ hạn chế được ứng dụng bị đóng băng cũng như nâng cao trải nghiệm người dùng.
Như đã nói ở phần 3, parallel programing là cần thiết trong lập trình giao diện.
Hãy so sánh hai đoạn mã sau, là đoạn mã viết cho sự kiện Click của Button1
a) Không sử dụng thread
procedure TForm1.Button1Click(Sender: TObject);
begin
// Do some thing here
ShowMessage('Xong');
end;
b) Sử dụng thread phụ
procedure TForm1.Button1Click(Sender: TObject);
var NewTask: ITask;
begin
NewTask := TTask.Create(procedure
begin
// Do some thing here
ShowMessage('Xong');
end);
NewTask.Start;
end;
Khác nhau:
Chúng ta khai báo một biến iTask, gán cho nó một số hoạt động bằng TTask.Create, sau đó gọi thủ tục Start.
Lưu ý rằng đoạn mã 2 chỉ tạo ra một thread mới để thực hiện công việc, và xóa thread (giải phóng - release) khi làm xong. Nó không thực sự hữu ích về tốc độ (chỉ sử dụng một sub thread) nhưng nó giúp tránh được lỗi đóng băng chương trình.