Threads
Cho đến hiện tại chúng ta đã quen với việc tạo ra các process con để xử lý các tác vụ song song nhau. Việc này tạo ra 2 vấn đề chính:
- Thứ nhất là mỗi lần khởi tạo process tốn rất nhiều thời gian vì nó copy các tài nguyên như memory hay file descriptors từ process cha.
- Thứ hai là việc truyền thông tin giữa các process khá phức tạp, yêu cầu các phương thức cụ thể trong IPC. Đối với thread thì khác, bản thân việc khởi tạo thread nhanh hơn process từ 10 đến 100 lần (theo sách), vì các threads tồn tại bên trong một process nên sử dụng rất nhiều tài nguyên chung, nhưng đổi lại nó cần cơ chế đồng bộ để bảo đảm.
Threads vs Process
- Creation speed: Như đã nói lúc trước, việc khởi tạo thread nhanh hơn rất nhiều so với process.
- Memory: Các dữ liệu của process là private, tồn tại và sử dụng ở nội bộ processs. Thread sinh ra là để sử dụng các tài nguyên chung.
- File descriptors: Bản thân file descriptor table ở các process là độc lập, nên đặt trường hợp cả 2 process cùng sử dụng một file, đóng file ở 1 process sẽ không ảnh hưởng đến process còn lại. Đối với thread thì khác, vì cùng nằm trong 1 process nên nếu đóng 1 file thì sẽ ảnh hưởng đến tất cả.
- Context Switching: Context Switch xảy ra khi CPU dừng một process và chuyển qua xử lý một process khác, điều này khiến CPU phải lưu state hiện tại để có thể quay về xử lý tiếp. Bởi vì process có address space riêng, nên context switching yêu cầu xóa hết toàn bộ nội dung của cache để sử dụng cho process mới. Đối với thread, việc này không xảy ra, nó chỉ cần làm mới các giá trị lưu trong registers (program counter, stack pointer, …).
- Communication: Như đã nói ở slide trước, giao tiếp giữa các process sẽ phức tạp hơn và yêu cầu IPC.
- Synchronization: Bản chất của process có tính isolation, còn thread thì sử dụng chung tài nguyên, nên thread yêu cầu tính đồng bộ cao hơn.
- Stability: Vì tất cả lý do trên nên sử dụng process sẽ ổn định hơn vì nếu một process gặp vấn đề thì sẽ không ảnh hưởng các process còn lại.
Shared vs Per-Thread Resources
- Shared:
- Signal Handlers & Disposition: Những instructions quyết định sẽ làm gì khi nhận được signal của hệ điều hành.
- Not shared:
- errno: Một biến toàn cục đặc biệt để lưu các error code (mã lỗi).
- signal mask & priority: Bản chất nó như một filter để xem thread hiện tại sẽ nhận signal nào.
Basic Thread Functions (creation)
Basic Thread Functions (Management)
- pthread_self: trả về ID của thread gọi hàm.
- pthread_joint: Tạm thời dừng thread hiện tại và đợi một thread cụ thể kết thúc.
- pthread_detach: Detach một thread, thead được detach sẽ tự giải phóng bộ nhớ sau khi thực thi xong và không thể join.
- pthread_exit: Kết thúc thread một cách chủ động
Thread-Specific Data
Vấn đề được đặt ra ở đây là các biến toàn cục ở trong một process sẽ được đọc ghi bởi toàn bộ thread. Nếu các thread cùng ghi vào 1 biến thì sẽ gây mất mát dữ liệu. Lưu ý rằng đây không phải vấn đề đồng bộ, chỉ là tổ chức dữ liệu một cách hiệu quả thôi.
Từ đó chúng ta có Thread-Specific Data. Về cơ bản thì đây là cơ chế giúp cho cùng một tên biến nhưng có thể trỏ đến các vị trí khác nhau trong bộ nhớ tùy thuộc vào thread nào đang sử dụng nó.
The APIs
- Creating the Key: Hàm khởi tạo key, chỉ nên được gọi 1 lần duy nhất. Destructor là một hàm sẽ được gọi khi thread kết thúc, vai trò của nó là để free dữ liệu đã được cấp phát trong TSD.
- Initialization: Hàm chỉ được gọi 1 lần để khởi tạo key. Biến onceptr lưu trạng thái để biết là hàm này đã được gọi bởi một thread nào đó hay chưa.
- getspecific và setspecific để truy cập vào dữ liệu lưu trong TSD.