Trong lập trình, đa luồng (multithreading) là một khái niệm quan trọng để tận dụng sức mạnh của các máy tính hiện đại với nhiều lõi CPU. Trong C++, chúng ta hoàn toàn có thể tận dụng đa luồng thông qua thư viện chuẩn std::thread. Trong bài viết này, chúng ta sẽ tìm hiểu về cách sử dụng đa luồng trong C++ và các ví dụ minh họa cụ thể.

1. Tại sao sử dụng Đa Luồng trong C++?

Trước khi đi vào cách sử dụng std::thread, hãy hiểu tại sao chúng ta cần sử dụng đa luồng. Máy tính hiện đại thường có nhiều lõi CPU, điều này có nghĩa là chúng có khả năng xử lý nhiều công việc đồng thời. Sử dụng đa luồng cho phép bạn chia nhỏ công việc thành các phần nhỏ hơn và thực hiện chúng đồng thời trên các lõi CPU khác nhau, giúp tăng hiệu suất và làm cho ứng dụng của bạn chạy nhanh hơn.

2. Quản lý Luồng trong C++

Trong quá trình phát triển ứng dụng đa luồng, quản lý luồng là một phần quan trọng. Bạn cần đảm bảo rằng các luồng của bạn hoạt động đồng bộ và không gây ra tình trạng đua nhau (race condition) hoặc deadlock. Các công cụ như mutex, semaphore, và condition variable được sử dụng để giải quyết các vấn đề này.

3. Sử dụng std::thread

3.1 Tạo và Khởi động Luồng

Để tạo một luồng, bạn cần truyền một hàm cho luồng đó chạy. Bạn có thể truyền hàm thông qua hàm constructor của std::thread.

#include <iostream>
#include <thread>
void myFunction() {
    std::cout << "Hello from thread!" << std::endl;
}
int main() {
    std::thread t1(myFunction); // Tạo một luồng và chạy hàm myFunction
    t1.join(); // Đợi luồng hoàn thành trước khi kết thúc chương trình
    return 0;
}

3.2 Truyền Tham số cho Luồng

Để truyền tham số cho luồng, bạn có thể truyền tham số đó vào hàm constructor của std::thread.

#include <iostream>
#include <thread>
void printMessage(const std::string& message) {
    std::cout << "Message: " << message << std::endl;
}
int main() {
    std::string message = "Hello from thread!";
    std::thread t1(printMessage, message);
    t1.join();
    return 0;
}

>>>>> Xem thêm bài viết tương tự tại đây:

4. Điều khiển Luồng trong C++

4.1 Phương thức join() trong C++

Phương thức join() được sử dụng để chờ đợi luồng hoàn thành trước khi tiếp tục thực thi chương trình.

#include <iostream>
#include <thread>
void workerFunction() {
    std::this_thread::sleep_for(std::chrono::seconds(2)); // Giả lập công việc mất thời gian
    std::cout << "Worker thread completed its task!" << std::endl;
}
int main() {
    std::thread workerThread(workerFunction);
    // Thực hiện một số công việc khác trong luồng chính
    std::cout << "Main thread is doing something..." << std::endl;
    workerThread.join(); // Đợi luồng workerThread hoàn thành
    std::cout << "Main thread finished!" << std::endl;
    return 0;
}

Kết quả:

Main thread is doing something...
Worker thread completed its task!
Main thread finished!

4.2 Phương thức detach() trong C++

Phương thức detach() cho phép bạn "tách" luồng ra khỏi luồng gọi, cho phép nó chạy độc lập.

#include <iostream>
#include <thread>
void workerFunction() {
    std::this_thread::sleep_for(std::chrono::seconds(2)); // Giả lập công việc mất thời gian
    std::cout << "Worker thread completed its task!" << std::endl;
}
int main() {
    std::thread workerThread(workerFunction);
    // Tách luồng workerThread ra khỏi luồng gọi (main thread)
    workerThread.detach();
    std::cout << "Main thread is doing something..." << std::endl;
    // Không cần gọi join() nữa
    std::cout << "Main thread finished!" << std::endl;
    return 0;
}

Kết quả:

Main thread is doing something...
Main thread finished!
Worker thread completed its task!  // Thông điệp này có thể hiện ra sau khi main thread đã kết thúc.

4.3 Phương thức native_handle() trong C++

Phương thức native_handle() trả về con trỏ tới bản gốc của luồng để thực hiện điều khiển tùy chỉnh.

#include <iostream>
#include <thread>
void workerFunction() {
    std::this_thread::sleep_for(std::chrono::seconds(2)); // Giả lập công việc mất thời gian
    std::cout << "Worker thread completed its task!" << std::endl;
}
int main() {
    std::thread workerThread(workerFunction);
    // Lấy con trỏ tới bản gốc của luồng
    std::thread::native_handle_type handle = workerThread.native_handle();
    // Sử dụng handle để thực hiện điều khiển tùy chỉnh, ví dụ: đặt ưu tiên của luồng
    // std::thread::native_handle_type là một kiểu dữ liệu không đồng nhất, do đó, 
    // việc sử dụng handle cần phải thực hiện cẩn thận và có thể không di động giữa các hệ thống.
    
    // Giả sử đặt ưu tiên của luồng thành 2 (ví dụ mục đích)
    int newPriority = 2;
    int oldPriority = pthread_getschedprio(handle);
    pthread_setschedprio(handle, newPriority);
    std::cout << "Changed thread priority from " << oldPriority << " to " << newPriority << std::endl;
    // Nếu không cần thiết, đảm bảo bạn đặt lại ưu tiên gốc sau khi hoàn thành công việc tùy chỉnh.
    workerThread.join();
    return 0;
}

Trong ví dụ trên, chúng ta đã sử dụng hàm pthread_getschedpriopthread_setschedprio từ thư viện POSIX để thay đổi ưu tiên của luồng, điều này chỉ là một ví dụ và có thể không di động giữa các hệ thống.

5. Ví dụ hoàn chỉnh

Dưới đây chúng ta sẽ cùng nhau xem một ví dụ hoàn chỉnh sử dụng các khái niệm trên:

#include <iostream>
#include <thread>
void printNumbers() {
    for (int i = 1; i <= 5; ++i) {
        std::cout << "Printing: " << i << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(500)); // Delay 500ms
    }
}
int main() {
    std::thread t1(printNumbers);
    std::cout << "Main thread doing something..." << std::endl;
    t1.join();
    std::cout << "Main thread finished!" << std::endl;
    return 0;
}

Tổng kết

Đa luồng là một khía cạnh quan trọng của lập trình hiện đại và C++ cung cấp các công cụ mạnh mẽ để chúng ta có thể tận dụng sức mạnh của nó. Bằng cách sử dụng std::thread, bạn có thể tạo và điều khiển các luồng một cách dễ dàng. Tuy nhiên, cần lưu ý rằng việc quản lý đa luồng có thể phức tạp và cần phải được thực hiện một cách cẩn thận để tránh lỗi và mắc kẹt.


Stringee Communication APIs là giải pháp cung cấp các tính năng giao tiếp như gọi thoại, gọi video, tin nhắn chat, SMS hay tổng đài CSKH cho phép tích hợp trực tiếp vào ứng dụng/website của doanh nghiệp nhanh chóng. Nhờ đó giúp tiết kiệm đến 80% thời gian và chi phí cho doanh nghiệp bởi thông thường nếu tự phát triển các tính năng này có thể mất từ 1 - 3 năm.

Bộ API giao tiếp của Stringee hiện đang được tin dùng bởi các doanh nghiệp ở mọi quy mô, lĩnh vực ngành nghề như TPBank, VOVBacsi24, VNDirect, Shinhan Finance, Ahamove, Logivan, Homedy, Adavigo, bTaskee…

Quý bạn đọc quan tâm xin mời đăng ký NHẬN TƯ VẤN TẠI ĐÂY:

Banner bottom