POSIX Thread (2)
Chúng ta hãy bắt đầu bằng một ví dụ đơn giản.
thread1.c
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
void *thread_function(void *arg)
{
int i;
for ( i=0; i<20; i++ ) {
printf("Thread says hi!\n");
sleep(1);
}
return NULL;
}
int main(void)
{
pthread_t mythread;
if ( pthread_create( &mythread, NULL, thread_function, NULL) ) {
printf("error creating thread.");
abort();
}
printf(“Waiting for thread to finish...\n”);
if ( pthread_join ( mythread, NULL ) ) {
printf("error joining thread.");
abort();
}
exit(0);
}
Biên dịch chương trình
$ gcc thread1.c -o thread1 -lpthread
thread1.c là một chương trình sử dụng thread rất đơn giản. Nó không thực sự làm gì hữu ích cả mà chỉ giúp chúng ta hiểu về cách thread làm việc.
Trong hàm main() chúng ta khai báo một biến là mythread có kiểu pthread_t. Kiểu pthread_t được định nghĩa trong pthread.h thường được gọi là thread id (viêt tắt là tid). Hãy nghĩ nó như là một dạng thread handle.
Sau khi mythread được khai báo (mythread chỉ là một “tid” hay một handle vào thread mà chúng ta sẽ khởi tạo), chúng ta gọi hàm pthread_create() để khởi tạo một thread thực sự. Hàm này sẽ trả về 0 nếu khởi tạo thread thành công và trả về một số khác 0 nếu thất bại. Hàm pthread_create có 4 tham số:
- tham số đầu tiên là một con trỏ tới mythread (&mythread),
- tham số thứ hai (trong chương trình được khởi tạo là NULL) có thể được sử dụng để xác định thuộc tính của thread. Vì thuộc tính mặc định của thread sử dụng tốt trong ví dụ của chúng ta nên chỉ đơn giản thiết lập nó là NULL,
- tham số thứ ba là tên của chương trình mà thread khởi tạo sẽ thực thi khi nó bắt đầu. Trong chương trình trên đó là hàm thread_function(). Khi hàm này trả về thì đồng nghĩa với việc thread mới sẽ dừng. Trong ví dụ trên thì nó chỉ đơn giản in ra “Thread says hi!” 20 lần và thoát ra. Chú ý rằng thread_function chấp nhận một con trỏ void* như là tham số và cũng trả về một trỏ void*. Có nghĩa là có thể truyền tham số cho thread mới và ở chương trình chính cũng có thể lấy được giá trị trả về của thread. Thế nhưng làm cách nào truyền tham số cho thread mới ở trong hàm pthread_create(). Nó được truyền ở
- tham số thứ tư của hàm pthread_create() dùng để truyền dữ liệu như là tham số thread mới. Trong ví dụ trên nó được khởi tạo NULL bởi vì chúng ta không cần truyền dữ liệu gì cho hàm thread_function().
Có thể thấy rằng ở chương trình trên bao gồm 2 threads sau khi hàm pthread_create() trả về thành công: một là thread mới tạo ra và một là thread của chương trình chính. Chương trình chính cũng được coi là một thread.
Sau khi thread mới được tạo ra thì thread chính vẫn tiếp tục thực thi lệnh tiếp theo trong chương trình (if ( pthread_join(…) )). Vì pthread_create tách thread chính thành 2 threads, nên pthread_join sẽ hợp 2 threads đó thành một. Tham số đầu tiên của pthread_join() là thread_id, mythread. Tham số thứ hai là một con trỏ trỏ vào một con trỏ void. Nếu con trỏ void không phải là NULL, thì pthread_join sẽ đặt giá trị trả về của thread_function (là một con trỏ void*) vào tham số thứ 2. Bởi vì trong ví dụ trên chúng ta không quan tâm về giá trị trả về của thead_function() nên chúng ta truyền vào NULL.
Hàm thread_function mất 20 giây để thực thi. Trước đó thì ở thread chính đã gọi pthread_join(), có nghĩa là thread chính đã bị block (sleep) và phải đợi thread_function() hoàn thành. Ngay khi thread_function kết thúc thì pthread_join() trả về. Lúc này thì chương trình chỉ còn thread chính. Khi chương trình kết thúc thì tất cả các thread mới sẽ phải được pthread_join.
Tham khảo: http://www.ibm.com/developerworks/linux/library/l-posix1.html?
Giờ mới đọc lọat bài này thấy anh Hoàng viết hay thế
Mỗi tội chương trình trên ko dịch được vì thiếu #include < stdio.h >
bai viet rat hay, rat chi tiet.
hay that
Người ta thường khuyên biên dịch với macro _REENTRANT. Tức là gọi biên dịch thế này:
Đọc vai trò của cái macro này ( http://pauillac.inria.fr/~xleroy/linuxthreads/faq.html#H ), em kg đủ trình hiểu. Anh Hoang Tran có thể giải thích cụ thể hơn ?
@bronzeboyvn: Sorry vì không hiểu sao cái hệ thống thông báo qua email khi có comment mới không work nên mình giờ mới biết câu hỏi của bạn. Đúng là lời khuyên là biên dịch với macro _REETRANT, đặc biệt trong những chương trình sử dụng glibc của Linux sử dụng C. Về cơ bản thì (trong cái link bạn đưa chỉ rất rõ
) rất nhiều hàm trong glibc được implement không phải cho multithread. Ví dụ trong stdio.h có các hàm getc hay putc. Bình thường nếu chương trình không multithread thì bạn sử dụng nó rất thoải mái, cứ việc get và put “c” thui. Tuy nhiên khi multithread thì bạn phải đồng bộ nó bởi vì cùng lúc bạn phải truy nhập IO mà. Giả sử một thread đang đợi để get một “c”, thread kia cũng vậy. Lúc đó sẽ xảy ra xung đột nên bạn phải đồng bộ các thread bằng mutex chẳng hạn. Ngoài ra trong glibc bạn sẽ thấy có nhiều biến toàn cục. Như biến errno (errno.h) chẳng hạn. Khi nhiều thread cùng truy nhập biến này thì theo nguyên tắc phải đồng bộ. Đó là sự phức tạp khi lập trình multithread.
Để tránh những trường hợp này thì người ta đã implement những hàm thay thế (như gethostname_r thay vì gethostname hoặc getc đã không được implement là macro như bình thường nữa) khi định nghĩa _REENTRANT khi biên dịch. Bạn hãy đọc kỹ cái link bạn post, cũng không quá khó hiểu đâu
vẫn khó hiểu “như xưa”, chắc em không có căn bản. 2 ý đầu em vẫn không rõ.Ý thứ 3 (errno), thì phỏng đoán thế này:
errno la biến toàn cục. Một lỗi thường gặp khi lập trình là :
Ở đây câu lệnh A nếu sai sẽ thay đổi giá trị erno, đáng ra ngay sau đó ta phải giữ giá tri erno tại thời diểm này lại, chang hạn int eval = erno; Nhưng ta lại thực hiện lệnh B, chẳng hạn int n = 1;.Đến khi truy xuất errno, thì chẳng thấy lỗi nào, bất chấp lenh A có gặp trục trặc hay không.
Như vậy errno, cung như mọi biến toàn cục khác là một trở ngại lớn thi lập trình multithread. Ở đây em đoán, macro _REENTRANT nó cung cấp một cơ chế nào đó để có thể theo dõi errno địa phương trong tung thread.
Một vấn đề hay khác đang chờ anh Tran Hoang viet tiếp, đó là vấn đề “n jobs, m threads”. Điều mong đợi ở đây không phải là đoạn code tắt mở mutex, để m threads thay nhau giải quyết công việc (mô phỏng mỗi jobs ở đây ta phải in ra màn hình câu “Hi, jobs $x”. Mà mong chờ y tuong anh thiết kế vấn đề này trên C++ (theo hướng đối tượng). Cai ma em đã được đọc ở bài viết số 3, rât sáng tạo. HOn nua vấn đề “n jobs, m threads” co ứng dụng rất lớn trong lập trình web cung như kết nối cơ sở dữ liệu. Có một mẫu thiết kế hương đối tuơng về vấn đề “n jobs, m threads”, thi khi viết mô hình client/server cứ vậy mà tương theo
@bronzeboyvn: Dự tính bài viết về POSIX thread còn rất dài, kể từ căn bản là những hàm C đến khi build được một thư viện C++ thread rồi thread pool đầy đủ, rồi phân tích cả những ưu nhược của các design-pattern khi thiết kế. Nhưng do mình lười quá mà cũng một phần nghĩ chắc không mấy ai hứng thú đề tài này
. Code thì đã được implement đầy đủ (vì mình build cái này cho một project của mình). Để lúc nào mình free rồi tiếp tục về đề tài này nhé
Anh Hoang Tran chỉ cần viết những khía cạnh mà anh cảm thấy có hứng thú, đạc sắc thôi, cần gì phải viết bài bản từ đầu đến cuối. Những kiến thức cơ bản, anh cứ để người đọc tự tìm hiểu. Khi có nhiều bài hay tự khắc nhiều người cảm thấy thích thú. Hơn nữa một blog mình cần 5,6 người đọc “hợp cạ” hơn là có 500, 600 bạn đọc dạng phổ thông.
mình mới tìm hiểu!
Sao khi mình chạy ở nhà trên visual 2005 thì nó lại báo lỗi fatal error C1083: Cannot open include file: ‘pthread.h’: No such file or directory . Mình không biết sửa sao hết!
các bạn có thể chỉ giùm không!
Hình như thư viện này chỉ có trên Linux.