STDIO
Tìm kiếm gần đây
    • Nội dung
    • QR Code
    • 0
    • 0
    • Sao chép

    std::thread trong C++

    Bài viết giúp người dùng nắm được cơ bản về thread, cách thức sử dụng thread với std::thread trong C++ (C++11). Bài viết này sẽ đưa đến cho người đọc kiến thức cơ bản để có thể lập trình đa luồng, từ đó có thể tự mình phát triển các kỹ thuật chuyên sâu hơn.
    19/09/2017
    18/09/2020
    5 phút đọc
    std::thread trong C++

    Khi quy mô của phần mềm bạn lập trình càng lớn thì vấn đề sử dụng tài nguyên sao cho hiệu quả càng quan trọng. Một trong những phương pháp để thực hiện điều này là thông qua việc sử dụng nhiều luồng chạy khác nhau, từ đó ta có thể hiện thực nhiều công việc đồng thời thay vì tuần tự như cách truyền thống. Bài viết này sẽ đưa đến cho người đọc kiến thức cơ bản để có thể lập trình đa luồng, từ đó có thể tự mình phát triển các kỹ thuật chuyên sâu hơn.

    Nhắc lại thread

    Thread là gì?

    Hiểu đơn thuần thread là một luồng chạy trong 1 chương trình (process), ta chỉ định công việc và hệ điều hành sẽ tự xử lý và thực hiện công việc đồng thời với luồng chạy chính.

    Cách tạo 1 thread và thực thi trong C++

    Tham khảo đoạn codes sau:

    #include <stdio.h>
    #include <thread>
    
    void SaySomething(const char* msg)
    {
    	printf("MSG = %s\n", msg);
    }
    
    int main()
    {
    	std::thread task(SaySomething, "Hello STDIO");
    	task.join();
    
    	return 0;
    }

    Đoạn code này định nghĩa một hàm là void SaySomething(const char* msg) sau đó hàm sẽ cho chạy thread vừa tạo thông qua dòng std::thread task(SaySomething, "Hello STDIO") trong đó SaySomething là tên hàm vừa tạo và "Hello STDIO" là tham số truyền vào. Và cuối cùng là hàm join được gọi, hàm này có chức năng tạm dừng chương trình lại và chờ đợi thread thực hiện xong. Nếu như bạn không muốn đợi thread thì bạn có thể thay hàm join bằng hàm detach, lúc này thread sẽ trở thành độc lập và tự bản thân hoạt động.

    Ví dụ về thread

    Tham khảo đoạn code sau về khả năng tính toán của thread:

    #include <thread>
    #include <Windows.h>
    #include <random>
    #include <time.h>
    
    #define MAT_SIZE 1000000
    #define ROW_SIZE 1000
    #define TRACK true
    
    void calc(int* Result_ptr, int* M2_T, int* M1_row_ptr, int start, int end) {
    	for (int M1_r = start; M1_r < end; ++M1_r) {
    		if (TRACK)
    			printf("calculating row %d\n", M1_r);
    		
    		int* M2_col_ptr = M2_T;
    		for (int M2_c = 0; M2_c < ROW_SIZE; ++M2_c) {
    
    
    			int result = 0;
    			for (int i = 0; i < ROW_SIZE; ++i) {
    				result += M1_row_ptr[i] * M2_col_ptr[i];
    			}
    			*Result_ptr++ = result;
    			M2_col_ptr += ROW_SIZE;
    		}
    		M1_row_ptr += ROW_SIZE;
    
    	}
    }
    
    void main() {
    
    	int* M1 = new int[MAT_SIZE]; // 1000 x 1000
    	int* M2_T = new int[MAT_SIZE]; // Ma tran chuyen vi cua M2 | 1000 x 1000
    	int* M_result_sequencep = new int[MAT_SIZE];
    	int* M_result_thread = new int[MAT_SIZE];
    
    	////////////////////////////////////////////////////////
    	////////// INIT ////////////////////////////////////////
    	////////////////////////////////////////////////////////
    
    	srand(0);
    	int* M1_ptr = M1;
    	int* M2_ptr = M2_T;
    	for (int i = 0; i < MAT_SIZE; ++i) {
    		*M1_ptr++ = rand() % 1000;
    		*M2_ptr++ = rand() % 1000;
    	}
    
    	////////////////////////////////////////////////////////
    	////////// SEQUENCE CALCULATION ////////////////////////
    	////////////////////////////////////////////////////////
    
    	int* M1_row_ptr = M1;
    	int* M2_col_ptr = M2_T;
    	int* Result_ptr = M_result_sequencep;
    
    	int start = clock();
    	for (int M1_r = 0; M1_r < ROW_SIZE; ++M1_r) {
    		if (TRACK)
    			printf("calculating row %d\n", M1_r);
    		M2_col_ptr = M2_T;
    		for (int M2_c = 0; M2_c < ROW_SIZE; ++M2_c) {
    			int result = 0;
    			for (int i = 0; i < ROW_SIZE; ++i) {
    				result += M1_row_ptr[i] * M2_col_ptr[i];
    			}
    			*Result_ptr++ = result;
    			M2_col_ptr += ROW_SIZE;
    
    		}
    		M1_row_ptr += ROW_SIZE;
    		
    	}
    	int end = clock();
    
    	/////////////////////////
    	printf("----\n");
    	////////////////////////////////////////////////////////
    	////////// THREAD CALCULATION //////////////////////////
    	////////////////////////////////////////////////////////
    	M1_row_ptr = M1;
    	M2_col_ptr = M2_T;
    	Result_ptr = M_result_thread;
    
    	int start_thread = clock();
    
    	//calc(int* Result_ptr, int* M2_T, int* M1_row_ptr, int start, int end)
    	std::thread t1(calc, Result_ptr + ROW_SIZE * ROW_SIZE / 4 * 0, M2_T, M1 + ROW_SIZE * ROW_SIZE / 4 * 0, ROW_SIZE / 4 * 0, ROW_SIZE / 4 * 1);
    	std::thread t2(calc, Result_ptr + ROW_SIZE * ROW_SIZE / 4 * 1, M2_T, M1 + ROW_SIZE * ROW_SIZE / 4 * 1, ROW_SIZE / 4 * 1, ROW_SIZE / 4 * 2);
    	std::thread t3(calc, Result_ptr + ROW_SIZE * ROW_SIZE / 4 * 2, M2_T, M1 + ROW_SIZE * ROW_SIZE / 4 * 2, ROW_SIZE / 4 * 2, ROW_SIZE / 4 * 3);
    	std::thread t4(calc, Result_ptr + ROW_SIZE * ROW_SIZE / 4 * 3, M2_T, M1 + ROW_SIZE * ROW_SIZE / 4 * 3, ROW_SIZE / 4 * 3, ROW_SIZE / 4 * 4);
    	t1.join();
    	t2.join();
    	t3.join();
    	t4.join();
    
    	int end_thread = clock();
    
    	////////////////////////////////////////////////////////
    	////////// OUTPUT CALCULATION //////////////////////////
    	////////////////////////////////////////////////////////
    
    	printf("Sequence time: %d\n", end - start);
    	printf("thread time: %d\n", end_thread - start_thread);
    	
    	return 0;
    }

    Đoạn code trên chính là chương trình nhân hai ma trận, kích thước của ma trận là 1000 x 1000 (lưu ý rằng để thuận tiện và tăng tốc độ cho cả 2 cách, tôi đã tổ chức M1 là ma trận hàng và M2 là ma trận cột).

    Khi chạy thử đoạn code, ta có thể thấy rằng với cách tính tuần tự, để nhân hai ma trận thì ta mất tầm 3.2 giây (tôi sử dụng máy dell core i3 – 2.4GHz, RAM 8GB), còn sử dụng 4 thread thì thời gian chỉ mất tầm 1.6 giây. Khi tăng kích thước ma trận lên 2000 x 2000 thì tính toán tuần tự mất 25.5 giây, còn tính toán sử dụng thread mất chỉ 11.7 giây.

    Một lưu ý khi sử dụng thread là ta không nên quá lạm dụng sử dụng thread do chi phí để tạo một thread là khá lớn, 1600 thread trống mất 1 giây, vì vậy khi tôi thử tạo một triệu thread khác nhau để nhân ma trận 1000 x 1000 thì lại mất thời gian khá lớn (gần 5 phút).

    0 Bình luận
    Modern C++

    Modern C++

    STDIO Training - Đào Tạo Lập Trình C++.

    Đề xuất

      Khi bạn nhấn vào sản phẩm do chúng tôi đề xuất và mua hàng, chúng tôi sẽ nhận được hoa hồng. Điều này hỗ trợ chúng tôi có thêm kinh phí tạo nhiều nội dung hữu ích. Tìm hiểu thêm.
      STDIO

      Trang chính

      Công ty TNHH STDIO

      30, Trịnh Đình Thảo, Hòa Thạnh, Tân Phú, Hồ Chí Minh
      +84 28.36205514 - +84 942.111912
      developer@stdio.vn

      383/1 Quang Trung, Phường 10, Quận Gò Vấp, Hồ Chí Minh
      Số giấy phép ĐKKD: 0311563559 do sở Kế hoạch và Đầu Tư TPHCM cấp ngày 23/02/2012

      Đã thông báo Bộ Công Thương
      ©STDIO, 2013 - 2020