Chương 8<br />
<br />
Lập trình<br />
đa luồng<br />
1<br />
<br />
8.1. Giới thiệu lập trình đa luồng<br />
Khai thác tính đa nhiệm, một chương trình có thể lập trình thực hiện nhiều<br />
phần việc đồng thời. Gọi là lập trình đa luồng (thread), có thể gọi đa tuyến.<br />
Luồng là quá trình thực hiện một đơn vị chương trình, độc lập với thực<br />
hiện các đơn vị khác trong chương trình đó.<br />
Mỗi luồng thường gắn với thực hiện một hàm nào đó trong chương trình,<br />
ta gọi hàm này là hàm luồng.<br />
Mỗi chương trình khi chạy luôn có một luồng ứng với thực hiện hàm chính<br />
của chương trình (WinMain), các luồng khác được tạo ra từ luồng này.<br />
<br />
<br />
luồng 3<br />
luồng 2<br />
luồng 1<br />
<br />
luồng 4<br />
<br />
luồng chính (main thread)<br />
thời gian<br />
2<br />
<br />
8.1. Giới thiệu lập trình đa luồng<br />
Mỗi luồng trong chương trình có mức độ ưu tiên thực hiện, là tài nguyên<br />
thời gian máy dành cho luồng.<br />
Ngoài ra mỗi luồng có các tài nguyên như stack, mức độ bảo mật,...<br />
Minh họa một chương trình đa luồng ứng với các hàm:<br />
<br />
<br />
Chương trình<br />
Hàm 1<br />
Hàm 2<br />
Hàm 3<br />
<br />
<br />
<br />
Luồng 1<br />
Luồng 2<br />
Luồng chính (main thread)<br />
Luồng 3<br />
Luồng 4<br />
<br />
Có hai loại luồng: luồng làm việc (worker) và luồng giao diện (user<br />
interface). Luồng làm việc chỉ chạy bên trong máy, còn luồng giao diện<br />
cung cấp những tương tác với người dùng.<br />
3<br />
<br />
8.2. Lập trình luồng làm việc<br />
<br />
<br />
Luồng làm việc được lập trình bởi một hàm gọi là hàm luồng, sau đó tạo<br />
luồng từ hàm này, gồm hai bước sau:<br />
<br />
<br />
<br />
<br />
<br />
<br />
Bước 1: Lập hàm xử lý luồng (hàm luồng), mẫu hàm khai báo như sau:<br />
UINT tên_hàm ( LPVOID tham_số );<br />
Trong đó tham số sẽ nhận các dữ liệu cho việc thực hiện bên trong hàm luồng,<br />
nó được truyền từ câu lệnh tạo luồng ở bước 2.<br />
Bước 2: Tạo luồng tại thời điểm cần thiết<br />
CWinThread* AfxBeginThread( tên_hàm_luồng , dữ_liệu_truyền );<br />
<br />
Có thể quy định các tham số như độ ưu tiên, độ lớn stack,... trong tham số<br />
của lệnh tạo luồng. Mẫu đầy đủ như sau:<br />
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc,<br />
LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT<br />
nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES<br />
lpSecurityAttrs = NULL );<br />
<br />
<br />
<br />
Lệnh tạo luồng trả về con trỏ đối tượng của luồng được tạo tương ứng,<br />
kiểu lớp CWinThread. Qua đối tượng này có thể tác động lên các luồng khi<br />
chạy.<br />
4<br />
<br />
8.3. Lập trình luồng giao diện<br />
<br />
<br />
<br />
MFC cung cấp một lớp cho việc lập trình luồng kiểu giao diện, có tên CWinThread,<br />
lớp này có phương thức ảo Run() để chạy ứng với luồng.<br />
Chúng ta xây dựng một lớp kế thừa CWinThread, viết đè hàm Run() để thực hiện<br />
luồng theo mẫu sau, lớp này phải có cơ chế tạo động – DYNCREATE.<br />
class tên_lớp_luồng : public CWinThread<br />
{ public: void Run()<br />
{<br />
..........lập trình hàm chạy luồng.............<br />
}<br />
DECLARE_DYNCREATE()<br />
};<br />
IMPLEMENT_DYNCREATE( tên_lớp_luồng , CWinThread )<br />
<br />
<br />
<br />
Viết lệnh tạo luồng sử dụng lớp trên tại thời điểm mong muốn<br />
CWinThread<br />
<br />
<br />
<br />
AfxBeginThread( RUNTIME_CLASS( tên_lớp_luồng ) );<br />
<br />
Ngoài ra có thể viết đè các phương thức khác để thực hiện theo yêu cầu như<br />
InitInsance(), ExitInstance(), OnIdle(),... Luồng giao diện giống như luồng chính<br />
của một ứng dụng.<br />
<br />
5<br />
<br />