Hướng dẫn Chi Tiết về pthread và Thuộc Tính Phân Tách trong Linux

Trong lập trình hệ thống Linux, đa luồng là một phương pháp quan trọng để thực hiện lập trình song song. Thư viện POSIX threads (pthread) cung cấp một bộ các giao diện thao tác với luồng, nắm vững cách sử dụng các giao diện này và cấu hình thuộc tính của luồng là nền tảng để viết các chương trình đa luồng hiệu quả và ổn định. Bài viết sẽ giải thích chi tiết về các hàm cơ bản của pthread và phân tích kỹ thuộc tính phân tách của luồng cũng như ứng dụng của nó.

Các Hàm Cơ Bản của pthread

1. pthread_create: Tạo Luồng

pthread_create là hàm tạo luồng, sau khi gọi thành công sẽ tạo ra một luồng mới và thực thi hàm nhiệm vụ được chỉ định.

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);
  • thread: con trỏ đến biến kiểu pthread_t, dùng để lưu ID của luồng mới.
  • attr: cấu hình thuộc tính của luồng, truyền NULL nếu sử dụng thuộc tính mặc định.
  • start_routine: hàm mục tiêu mà luồng sẽ thực thi, có kiểu void *(*)(void *).
  • arg: đối số được truyền vào hàm start_routine.

Giá trị trả về:

  • Trả về 0 nếu thành công.
  • Trả về mã lỗi khác 0 nếu thất bại.

2. pthread_exit: Kết thúc Luồng

pthread_exit được sử dụng để kết thúc một luồng một cách chủ động, có thể chỉ định giá trị trả về của luồng.

void pthread_exit(void *retval);
  • retval: giá trị trả về của luồng, kiểu void*.

Lưu ý:

  • Nếu luồng chính gọi pthread_exit, chỉ có luồng chính kết thúc còn các luồng con vẫn tiếp tục.
  • Khi hàm của luồng kết thúc bằng cách return thì tương đương với gọi pthread_exit.

3. pthread_join: Chờ và Thu Hồi Luồng

pthread_join là hàm chờ đợi một luồng cụ thể kết thúc và thu hồi tài nguyên của nó, đồng thời có thể lấy trạng thái thoát của luồng.

int pthread_join(pthread_t thread, void **retval);
  • thread: ID của luồng cần chờ đợi.
  • retval: con trỏ đến void*, dùng để lưu giá trị trả về của luồng.

Giá trị trả về:

  • Trả về 0 nếu thành công.
  • Trả về mã lỗi khác 0 nếu thất bại.

Mục đích chính:

  • Thu hồi tài nguyên của luồng: tránh tình trạng "luồng zombie" (luồng kết thúc nhưng không giải phóng tài nguyên).
  • Đồng bộ hóa luồng: luồng chính có thể chờ luồng con hoàn thành trước khi tiếp tục.

4. pthread_attr_init: Khởi Tạo Thuộc Tính Luồng

pthread_attr_init được sử dụng để khởi tạo một đối tượng thuộc tính luồng, thiết lập nó về các giá trị mặc định.

int pthread_attr_init(pthread_attr_t *attr);
  • attr: con trỏ đến đối tượng thuộc tính luồng kiểu pthread_attr_t.

Giá trị trả về:

  • Trả về 0 nếu thành công.
  • Trả về mã lỗi khác 0 nếu thất bại.

5. pthread_attr_destroy: Giải Phóng Thuộc Tính Luồng

pthread_attr_destroy được sử dụng để giải phóng đối tượng thuộc tính luồng đã được khởi tạo.

int pthread_attr_destroy(pthread_attr_t *attr);
  • attr: đối tượng thuộc tính luồng đã khởi tạo.

Giá trị trả về:

  • Trả về 0 nếu thành công.
  • Trả về mã lỗi khác 0 nếu thất bại.

6. pthread_attr_setdetachstate: Thiết Lập Thuộc Tính Phân Tách

pthread_attr_setdetachstate là một trong những hàm thiết lập thuộc tính quan trọng, dùng để thiết lập thuộc tính "được nối / phân tách" của luồng.

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
  • attr: đối tượng thuộc tính luồng đã khởi tạo.
  • detachstate: trạng thái phân tách của luồng, có thể là:
    • PTHREAD_CREATE_DETACHED: thuộc tính phân tách.
    • PTHREAD_CREATE_JOINABLE: thuộc tính được nối (giá trị mặc định).

Giá trị trả về:

  • Trả về 0 nếu thành công.
  • Trả về mã lỗi khác 0 nếu thất bại.

Phân Tích Thuộc Tính Phân Tách Của Luồng

Thuộc tính phân tách (detachstate) của luồng xác định cách hệ thống thu hồi tài nguyên sau khi luồng kết thúc, là một thuộc tính quan trọng trong lập trình đa luồng.

So Sánh Hai Thuộc Tính

Loại Thuộc Tính Tính Chất Chính Ứng Dụng
Thuộc tính Được Nối (JOINABLE) Sau khi kết thúc, cần gọi pthread_join để thu hồi tài nguyên; có thể lấy trạng thái thoát; đồng bộ hóa Trường hợp cần đợi kết quả của luồng
Thuộc tính Phân Tách (DETACHED) Sau khi kết thúc, hệ thống tự động thu hồi tài nguyên; không cần gọi pthread_join; không thể lấy trạng thái thoát Các tác vụ nền không cần đợi kết quả

Lưu Ý Quan Trọng

  • Nếu luồng có thuộc tính JOINABLE nhưng không gọi pthread_join, sau khi kết thúc sẽ trở thành "luồng zombie", chiếm tài nguyên hệ thống.
  • Gọi pthread_join trên luồng có thuộc tính DETACHED sẽ trả về lỗi (EINVAL).
  • Có thể sử dụng hàm pthread_detach để thiết lập thuộc tính phân tách sau khi tạo luồng (không cần cấu hình đối tượng thuộc tính trước đó).

Ví Dụ Thực Hiện: Tạo và Thu Hồi Các Loạt Thuộc Tính Luồng

Ví Dụ 1: Luồng Mặc Định (JOINABLE)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

void *thuc_hanh(void *tham_so) {
    int so = *(int *)tham_so;
    printf("Luồng %d đang chạy\n", so);
    pthread_exit((void *)(so + 100));
}

int main() {
    pthread_t id;
    int tham_so = 1;
    void *ket_qua;

    int ret = pthread_create(&id, NULL, thuc_hanh, &tham_so);
    if (ret != 0) {
        perror("pthread_create thất bại");
        exit(1);
    }

    ret = pthread_join(id, &ket_qua);
    if (ret != 0) {
        perror("pthread_join thất bại");
        exit(1);
    }
    printf("Luồng kết thúc, kết quả: %ld\n", (long)ket_qua);

    return 0;
}

Ví Dụ 2: Luồng Phân Tách (DETACHED)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

void *thuc_hanh_detached(void *tham_so) {
    int so = *(int *)tham_so;
    printf("Luồng phân tách %d đang chạy\n", so);
    sleep(2);
    printf("Luồng phân tách %d kết thúc\n", so);
    return NULL;
}

int main() {
    pthread_t id;
    pthread_attr_t thuoc_tinh;
    int tham_so = 2;

    int ret = pthread_attr_init(&thuoc_tinh);
    if (ret != 0) {
        perror("pthread_attr_init thất bại");
        exit(1);
    }

    ret = pthread_attr_setdetachstate(&thuoc_tinh, PTHREAD_CREATE_DETACHED);
    if (ret != 0) {
        perror("pthread_attr_setdetachstate thất bại");
        exit(1);
    }

    ret = pthread_create(&id, &thuoc_tinh, thuc_hanh_detached, &tham_so);
    if (ret != 0) {
        perror("pthread_create thất bại");
        exit(1);
    }

    pthread_attr_destroy(&thuoc_tinh);

    sleep(3);
    printf("Luồng chính kết thúc\n");

    return 0;
}

Biên Dịch và Chạy Chương Trình

Khi biên dịch cần liên kết thư viện pthread (-lpthread):

gcc thread_vd.c -o thread_vd -lpthread
./thread_vd

Thẻ: pthread multithreading linux POSIX

Đăng vào ngày 2 tháng 7 lúc 15:24