Xem mẫu

I.1 Tạo lập thread. I.1.1. Thiết lập Thuộc tính cho thread. 1) pthread_attr_init(&attr); 2) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 3) pthread_create(&threads[0], &attr, inc_count, (void*)&thread_ids[0]); Lệnh 1) tạo ra biến attr để nhớ thuộc tính, lệnh 2) nạp thuộc tính cho nó – ở đây thuộc tính được chỉ định là “PTHREAD_CREATE_JOINABLE”, lệnh 3) chỉ định thread có thuộc tính attr. Chúng ta thường sử dụng thuộc tính ngầm định NULL, chỉ thị này cho phép các thread “join” được với trình chính. I.1.2. Tạo lập, Giao nhiệm vụ & Truyền tham số cho thread. I.1.2.A Các bước cơ bản. Để tạo lập và điều kiển một thread ta cần phải thực hiện 5 bước cơ bản sau 1. Mô tả thread: void * proc_1(int x,y) 2. Đăng ký thread: pthread_t tid; 3. Khởi tạo (đăng ký thuộc tính), giao nhiệm vụ, và truyền dữ liệu. pthread_create(&tid, NULL, &proc_1, (void*)&arg); 4. Xác định thread pthread_equal(pthread_self(), ....) 5. Thu hồi pthread_join(tid, NULL); “Mô tả thread” là việc tạo ra một trình con. Nó chính là thread sau này. Các lệnh còn lại chỉ là thủ tục để cho trình con này biến thành thread, tức là chạy song song và có các thuộc tính cần thiết. Các lệnh này gồm có 1) Lệnh khởi tạo và giao nhiệm vụ pthread_create Lệnh này kết nối một thread với một hàm con (được gọi tắt là thread1, hay hàm được kích hoạt thành thread). Lệnh còn đảm nhận trách nhiệm truyền dữ liệu cho thread. Dữ liệu phải được khai báo trước và để ở đâu đó trong bộ nhớ. Tuy nhiên không phải các dữ liệu này được truyền cho thread, mà chỉ là địa chỉ của những khối dữ liệu cần phải truyền, mà thôi. Các địa chỉ là các con trỏ – nó có thể là địa chỉ của một vùng nhớ, và cũng có thể là biến địa chỉ chứa địa chỉ của vùng nhớ có dữ liệu. Ví dụ, nếu cần truyền giá trị chứa trong biến X thì (do địa chỉ của X là &X) chỉ thị truyền là (void*)&X. Còn nếu địa chỉ của dữ liệu được để trong con trỏ P thì chỉ thị truyền là (void*)P. Do chỉ được truyền cho địa chỉ, cho nên, để có thể sử dụng được những dữ liệu này, thread phải tự định dạng các dữ liệu này sao cho hợp với dạng vốn có của chúng. 2) Lệnh kết thúc hoạt động pthread_join Lệnh này đóng vai trò đồng bộ thread với trình chính2. Số là, tiến trình chính, sau khi khởi tạo ra các thread, thì vẫn tiếp tục hoạt động – và các thread do nó tạo ra thì chạy song song với nó. Nếu trình chính mà muốn sử dụng một kết quả nào đó do thread tính ra, thì nó phải biết được thời điểm mà thread kết thúc hoạt động của mình. Trình chính không thể tự biết được thời điểm các thread kết thúc hoạt động, vì vậy cần phải có một lệnh – và đó chính là lệnh pthread_join – bắt trình chính (nếu nó đến lệnh này sớm hơn) phải chờ để thread kết thúc công việc của mình. Điều này hàm ý, ở lệnh tiếp theo sau lệnh nó, thread tương ứng đã không còn nữa3. Lệnh pthread_joint chỉ thị chờ cho từng thread một, và chính vì vậy trong chỉ thị lệnh phải có thông báo tên của thread cần “join” với trình chính – hay trình chính phải chờ. I.1.2.B Ví dụ về tạo lập & truyền dữ liệu cho thread. Ví dụ 1. Tạo lập thread. Trình chính (thread chính) tạo ra một thread để làm một việc gì đó cho mình. Trong ví dụ này thread in “Hello, ” còn trình chính in “World!”. Do thread chạy song song với trình chính mà các từ in ra không theo thứ tự nhất định, nó có thể là “Hello World” và cũng có thể là “World Hello”. #include #include void * print_hello(void *args) { cout<<"Hello, ";return NULL;} void *main() 1 Trên thực tế mỗi khi lệnh này được gọi thì có 2 thread được tạo ra. Thread “con” dùng để chạy trình con và thread “mẹ” để chạy trình chính. Như vậy thread và trình chính đều là các thread. Thread “con” có thể có được những tính chất như quyền ưu tiên, lịch hoạt động, thời điểm kết thúc hoạt động (đồng bộ hay không)... 2 Thời điểm kết thúc hoạt động của một thread có thể là đồng bộ hay là không đồng bộ. Nếu một thread được khai báo là “kết hợp” (joined) thì nó chỉ thực sự kết thúc hoạt động (hủy bỏ hoàn toàn bộ nhớ do nó tạo ra như stack và header) khi lệnh trình chính gọi lệnh “pthread_join()”. Ngược lại nếu nó thuộc loại “tách rời” (detached) thì nó sẽ giải phóng tất cả bộ nhớ do nó tạo ra khi nó kết thúc hoạt động 3 Lưu ý lệnh này không hủy bỏ vùng bộ nhớ mà thread đã xin cấp. { pthread_t tid; pthread_create(&tid, NULL, &print_hello, NULL); cout<<"World! "; pthread_join(tid, NULL); return NULL; } Ví dụ 2. Truyền dữ liệu. Do đặc thù của tính song-song trong xử lý mà chúng ta cần phải tin chắc rằng khoảng thời gian kể từ lúc một thread được tạo ra cho đến lúc nó hoạt động là phải được khống chế và là phải là không đáng kể. Điều này hàm ý là tổng thời gian thực tế cần thiết để truyền dữ liệu cho nó phải là rất ít – diễn ra trong khoảng một vài lệnh asambly. Như vậy, giao nhiệm vụ cho thread cũng như cấp dữ liệu cho nó đều là thông qua con trỏ. Con trỏ sẽ trỏ tới địa chỉ, nơi lưu giữ trình con cần phải được kích hoạt để chạy song song; con trỏ sẽ trỏ tới vùng dữ liệu nơi lưu giữ dữ liệu cần cho thread. Tóm lại nguyên lý truyền dữ liệu cho một thread là “truyền con trỏ đến vùng dữ liệu”. Sau đây là ví dụ về việc truyền nói trên. Hàm được kích hoạt là “Say”. Đó là một hàm được khai báo ở dạng con trỏ, và chính con trỏ này sẽ được truyền cho thread để thông báo hàm cần phải được kích hoạt. Hàm này sẽ in ra màn hình lời chào bằng ở các ngôn ngữ khác nhau. Dữ liệu cần truyền cho thread để nó hoạt động là các dòng chữ và chúng ta không truyền hẳn cho các thread cả dòng chữ, bở dung lượng dữ liệu phải truyền sẽ rất nhiều vì các dòng này tương đối dài, mà chúng ta chỉ truyền cho nó địa chỉ nơi lưu giữ các lời chào đó. Trong ví dụ sau, trình chính tạo ra và truyền dữ liệu cho 3 thread chạy song song. Mỗi thread in ra một dòng thông báo trên màn hình. Các dòng chữ đó là các câu chào (đã để sẵn trong bộ nhớ, trong một mảng) ở các ngôn ngữ khác nhau: "English: Hello World!", "French: Bonjour, le monde!", "Vietnam: Chao tat ca". Để làm được việc này, mỗi thread nhận được địa chỉ ứng với vị trí dòng chữ tương ứng. #include #include "pthread.h" #include using namespace std; #define NUM 3 string mess[NUM]; void *Say(void *messages) { cout<< (char *) messages << "\n"; pthread_exit(NULL); return NULL; } void *main(int argc, char *argv[]) { pthread_t threads[NUM]; mess [0] = "English: Hello World!"; mess [1] = "French: Bonjour, le monde!"; mess [2] = "Vietnam: Chao tat ca!"; for(int t=0;t #include #include "pthread.h" #define NUM 3 char *messages[NUM]; void *PrintHello(void *threadid) { int *id_ptr, taskid; id_ptr = (int *) threadid; taskid = *id_ptr; printf("Thread %d: %s\n", taskid, messages[taskid]); pthread_exit(NULL); return NULL; } void main(int argc, char *argv[]) { pthread_t threads[NUM]; int *taskids[NUM]; int t; messages[0] = "English: Hello World!"; messages[1] = "French: Bonjour, le monde!"; messages[2] = "Vietnam: Chao tat ca!"; for(t=0;t #include "pthread.h" #include #define NUM 3 using namespace std; string mess[NUM]; struct wrapper { int id; string message; } data_array [NUM]; void * PrintHello(void* data) { cout<< ((wrapper*)data)->id << " --> "; cout<< ((wrapper*)data)->message.c_str() << "\n"; return NULL; } void *main() { pthread_t threads[NUM]; for(int t=0;t Thread 0 1 --> Thread 1 2 --> Thread 2 */ Ví dụ 5. Các thread có thể là hàm của một class, nhưng hàm này phải thuộc loại được khai báo “static”. #include #include "pthread.h" class Hello{ public: static void* print(void* msg); ... - tailieumienphi.vn
nguon tai.lieu . vn