Khóa luận Nghiên cứu lập trình thread và ứng dụng

MỤC LỤC

CHƯƠNG 1: GIỚI THIỆU VỀTHREAD VÀ MULTI THREAD.7

1.1. Tổng quan vềthread .7

1.2. So sánh thread với tiến trình .7

1.3. Đa thread: những lợi thế.8

1.4. Tiến trình, thread nhân, thread người dùng, fiber.9

1.5. Vấn đề đưa ra của thread và fiber .10

1.5.1.Truy cập đồng thời và cấu trúc dữliệu. 10

1.5.2.Vào/ ra và bộlập lịch. 11

1.6. Các mô hình .12

1.6.1. Mô hình 1:1 (thread cấp nhân). 12

1.6.2. Mô hình N:1 (thread cấp người dùng). 12

1.6.3. Mô hình N:M (thread tích hợp). 12

1.7. Ngôn ngữhỗtrợ.13

 

CHƯƠNG 2: POSIX THREAD PROGRAMMING .14

2.1. Tổng quan vềPthread .14

2.1.1. Khái niệm Pthread. 14

2.1.2. Tại sao lại sửdụng Pthread?. 14

2.1.3. Pthread API. 16

2.1.4. Biên dịch chương trình Threaded. 17

 

Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng

 

 

Sinh viên: Cấn Việt Dũng 4 Lớp : K51CHTTT

 

2.2. Quản lý Thread .18

2.2.1. Các thủtục chính. 18

2.2.2. Tạo Thread. 18

2.2.3. Thiết lập các thuộc tính cho Thread. 19

2.2.4. Hủy thread. 19

2.2.5. Truyền tham sốcho Thread. 21

2.2.6. Nối và tách Thread. 22

2.2.6.1. Những thủtục chính .23

2.2.6.2. Nối Thread.23

2.2.6.3. Có thểnối được hay không?.23

2.2.6.4. Tách (detaching) .24

2.2.7. Quản lý stack. 26

2.2.7.1. Những thủtục .26

2.2.7.2. Ngăn ngừa những vấn đềvới stack .26

2.3. Biến Mutex .26

2.3.1. Khái niệm mutex. 26

2.3.2. Tạo ra và phá hủy mutex. 27

2.3.2.1. Những thủtục .27

2.3.2.2. Cách sửdụng .28

2.3.3. Khóa và mởkhóa mutex. 28

2.3.3.1. Các thủtục .28

2.3.3.2. Cách sửdụng .28

2.4. Biến điều kiện .33

2.4.1. Khái niệm vềbiến điều kiện. 33

 

Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng

 

 

Sinh viên: Cấn Việt Dũng 5 Lớp : K51CHTTT

 

2.4.2. Tạo ra và phá hủy 1 biến điều kiện. 35

2.4.2.1. Các thủtục .35

2.4.2.2. Cách sửdụng .35

2.4.3. Waiting và signaling trên biến điều kiện. 35

2.4.3.1. Các thủtục .36

2.4.3.2. Cách sửdụng .36

2.5. Dữliệu riêng của Thread(Thread – specific data) .39

2.5.1. Khái niệm dữliệu riêng của thread. 39

2.5.2. Cấp phát dữliệu riêng của thread. 39

2.5.3. Truy cập vào dữliệu riêng của thread. 40

2.5.4.Xóa dữliệu trong thread. 42

 

CHƯƠNG 3: BÀI TOÁN CLOSEST_PAIRTRONG KHÔNG GIAN HAI CHIỀU SỬ

DỤNG MULTITHREADING.43

3.1. Giới thiệu bài toán .43

3.2. Các thuật toán khác nhau đểgiải bài toán tìm khoảng cách ngắn nhất giữa các cặp

điểm trong N điểm cho trước.43

pdf60 trang | Chia sẻ: oanh_nt | Lượt xem: 2820 | Lượt tải: 4download
Bạn đang xem trước 20 trang tài liệu Khóa luận Nghiên cứu lập trình thread và ứng dụng, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
trong thủ tục pthread_create: ƒ Thread: một định danh duy nhất cho một thread mới và được trả về bởi thủ tục con. Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 19 Lớp : K51CHTTT ƒ Attr: một đối tượng thuộc tính có thể được sử dụng để thiết lập các thuộc tính cho thread. Bạn có thể xác định một đối tượng thuộc tính thread, hoặc để NULL với giá trị mặc định. ƒ Start_routine: thủ tục C để thread sẽ thực thi một lần khi nó được tạo ra. ƒ Arg: một tham số đơn để có thể được truyền cho start_routine. Nó phải được truyền bởi tham chiếu như là con trỏ kiểu void. Mỗi một lần được tao ra, thread có thể tạo ra các thread khác. Không có hệ thống cập bậc hoặc phụ thuộc giữa các thread. 2.2.3. Thiết lập các thuộc tính cho Thread Mặc định, một thread được tạo ra với một thuộc tính cố định. Một vài trong số những thuộc tính có thể được thay đổi bởi người lập trình thông qua đối tượng thuộc tính thread. Pthread_attr_init và pthread_attr_destroy được sử dụng để khởi tạo hoặc phá hủy đối tượng thuộc tính thread. Những thủ tục khác được sử dụng sau đó để truy vấn hoặc thiết lập những thuộc tính xác định trong đối tượng thuộc tính thread. 2.2.4. Hủy thread Có một vài cách mà trong đó Pthread bị hủy: ƒ Những thread mà trả về từ thủ tục bắt đầu của nó ( thủ tục chính với thread ban đầu). Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 20 Lớp : K51CHTTT ƒ Những thread thực hiện lời gọi tới hàm pthread_exit. ƒ Những thread bị hủy từ những thủ tục khác bằng hàm pthread_cancel. ƒ Tiến trình cuối bị ngắt trong khi thực hiện lời gọi tới hàm khác hoặc thoát khỏi thủ tục con. Hàm pthread_exit được sử dụng để thoát khỏi một thread. Thông thường, thủ tục pthread_exit() được gọi sau khi 1 thread đã hoàn thành công việc của nó và không còn yêu cầu nào. Nếu hàm main() kết thúc trước khi những thread được tạo ra và thoát với hàm pthread_exit(), những thread khác vẫn tiếp tục được thực hiện. Ngoài ra, chúng cũng sẽ tự động bị ngắt khi hàm main() kết thúc. Người lập trình có thể tùy chọn chỉ định một trạng thái (status) ngắt, được lưu như một con trỏ kiểu void cho bất kỳ thread nào để có thể tham gia vào lời gọi thread. Tóm lại: thủ tục pthead_exit() không phải để đóng file, bất kỳ file nào được mở bên trong thread, các file này sẽ vẫn mở sau khi thread bị ngắt. Ví dụ về tạo thread và ngắt thread: ví dụ này tạo ra 5 thread với thủ tục pthread_create(). Mỗi thread sẽ in ra thông điệp “Hello world” và sau đó được ngắt bới lời gọi hàm pthread_exit(): #include #include #define NUM_THREADS 5 void *PrintHello(void *threadid) { long tid; tid = (long)threadid; printf("Hello World! It's me, thread #%ld!\n", tid); pthread_exit(NULL); } int main (int argc, char *argv[]) { pthread_t threads[NUM_THREADS]; Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 21 Lớp : K51CHTTT int rc; long t; for(t=0; t<NUM_THREADS; t++){ printf("In main: creating thread %ld\n", t); rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t); if (rc){ printf("ERROR; return code from pthread_create() is %d\n", rc); exit(-1); } } pthread_exit(NULL); } 2.2.5. Truyền tham số cho Thread Thủ tục pthread_create() cho phép người lập trình có thể truyền một đối số cho thủ tục khởi tạo thread. Trong trường hợp có nhiều tham số cần truyền, sự giới hạn này rất dễ dàng được vượt qua bằng cách tạo ra 1 cấu trúc struct bao gồm tất cả các tham số, sau đó truyền 1 tham số con trỏ của struct này vào thủ tục pthread_create(). Tất cả các tham số đều phải được truyền bằng tham chiếu và kiểu (void *). Ví dụ về cách truyền tham số: Ví dụ 1: truyền 1 tham số: long *taskids[NUM_THREADS]; for(t=0; t<NUM_THREADS; t++) { taskids[t] = (long *) malloc(sizeof(long)); *taskids[t] = t; printf("Creating thread %ld\n", t); rc = pthread_create(&threads[t], NULL, PrintHello,(void *) taskids[t]); ... } Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 22 Lớp : K51CHTTT Ví dụ 2: Truyền nhiều tham số sử dụng struct. struct thread_data{ int thread_id; int sum; char *message; }; struct thread_data thread_data_array[NUM_THREADS]; void *PrintHello(void *threadarg) { struct thread_data *my_data; ... my_data = (struct thread_data *) threadarg; taskid = my_data->thread_id; sum = my_data->sum; hello_msg = my_data->message; ... } int main (int argc, char *argv[]) { ... thread_data_array[t].thread_id = t; thread_data_array[t].sum = sum; thread_data_array[t].message = messages[t]; rc = pthread_create(&threads[t], NULL, PrintHello, (void *) &thread_data_array[t]); ... } 2.2.6. Nối và tách Thread Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 23 Lớp : K51CHTTT 2.2.6.1. Những thủ tục chính ƒ Pthread_join (threadid,status) ƒ Pthread_detach(threadid,status) ƒ Pthread_attr_setdetachstate(attr,detachstate) ƒ Pthread_attr_getdetachstate(attr,detachstate) 2.2.6.2. Nối Thread Nối (joining) là 1 cách để thực hiện việc đồng bộ hóa giữa các thread. Ví dụ: Thủ tục con pthread_join() khóa lời gọi thread cho tới khi threadid xác định bị ngắt. Người lập trình cũng có thể nhận được trạng thái ngắt trả về của thread mục tiêu nếu nó được xác định từ lời gọi pthread_exit(). Một thread đang được nối có thể gặp lời gọi pthead_join(). Nó là lỗi logic khi cố thử nhiều phép nối trên cùng một thread. Hai phương thức đồng bộ hóa khác là mutexes và biến điều kiện sẽ được thảo luận ở phần sau. 2.2.6.3. Có thể nối được hay không? Khi một thread được tạo ra, một trong những thuộc tính của nó định nghĩa khi nào thì có thể nối được hoặc tách được. Chỉ thread mà được tạo ra với thuộc tính là có nối được thì mới có thể được nối. Nếu một thread được tạo ra với thuộc tính là tách, nó không bao giờ được nối. Bản nháp cuối cùng của chuẩn POSIX xác định rằng các thread nên được tạo ra với thuộc tính có thể nối được, để rõ ràng khi tạo ra một thread có thể nối hoặc tách, tham số attr trong thủ tục pthread_create() được sử dụng. Quá trình có 4 bước là: Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 24 Lớp : K51CHTTT ƒ Khai báo một biến thuộc tính của pthread với kiểu dữ liệu là pthread_attr_t ƒ Khởi tạo biến thuộc tính với hàm pthread_attr_init() ƒ Thiết lập thuộc tính tách được với hàm pthread_attr_setdetachstate() ƒ Khi đã hoàn thành, giải phóng tài nguyên được sử dụng bởi các thuộc tính với hàm pthread_attr_destroy(). 2.2.6.4. Tách (detaching) Thủ tục pthread_detach() có thể được sử dụng để tách một thread thậm chí thread đó được tạo ra với thuộc tính là có thể nối được.Không có thủ tục ngược lại. Ví dụ về nối thread: #include #include #include #define NUM_THREADS 4 void *BusyWork(void *t) { int i; long tid; double result=0.0; tid = (long)t; printf("Thread %ld starting...\n",tid); for (i=0; i<1000000; i++) { result = result + sin(i) * tan(i); } printf("Thread %ld done. Result = %e\n",tid, result); pthread_exit((void*) t); } int main (int argc, char *argv[]) { Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 25 Lớp : K51CHTTT pthread_t thread[NUM_THREADS]; pthread_attr_t attr; int rc; long t; void *status; /* Initialize and set thread detached attribute */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); for(t=0; t<NUM_THREADS; t++) { printf("Main: creating thread %ld\n", t); rc = pthread_create(&thread[t], &attr, BusyWork, (void *)t); if (rc) { printf("ERROR; return code from pthread_create() is %d\n", rc); exit(-1); } } /* Free attribute and wait for the other threads */ pthread_attr_destroy(&attr); for(t=0; t<NUM_THREADS; t++) { rc = pthread_join(thread[t], &status); if (rc) { printf("ERROR; return code from pthread_join() is %d\n", rc); exit(-1); } printf("Main: completed join with thread %ld having a status of %ld\n",t,(long)status); } printf("Main: program completed. Exiting.\n"); pthread_exit(NULL); } Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 26 Lớp : K51CHTTT 2.2.7. Quản lý stack 2.2.7.1. Những thủ tục ƒ Pthread_attr_getstacksize(attr, stacksize) ƒ Pthread_attr_setstacksize(attr, stacksize) ƒ Pthread_attr_getstackaddr(attr, stackaddr) ƒ Pthread_attr_setstackaddr(attr, stackaddr) 2.2.7.2. Ngăn ngừa những vấn đề với stack Chuẩn POSIX không quy định kích thước stack của một thread. Điều này phụ thuộc và thực hiện khác nhau. Vượt giới hạn mặc định của stack rất dễ làm, với một kết quả bình thường: chương trình chấm dứt hoặc dữ liệu bị hỏng. Một chương trình an toàn và di động không phụ thuộc vào giới hạn mặc định của stack, nhưng thay vào đó, rõ ràng stack bố trí đủ cho mỗi thread bằng cách sử dụng thủ tục pthread_attr_setstacksize. Các thủ tục pthread_attr_getstackaddr và pthread_attr_setstackaddr có thể được sử dụng bởi những ứng dụng trong một môi trường mà ở đó stack cho mỗi thread phải đặt ở một vài vùng cụ thể của bộ nhớ. 2.3. Biến Mutex 2.3.1. Khái niệm mutex Mutex là viết tắt của “mutual exclusion”. Biến mutex là một trong những phương tiện chính để thực hiện việc đồng bộ thread và cho việc bảo vệ việc chia sẻ dữ liệu khi xảy ra nhiều lời viết. Một biến mutex hoạt động như một “khóa” bảo vệ quyền truy cập vào một nguồn dữ liệu được chia sẻ. Khái niệm cơ bản của một mutex được sử dụng trong Pthread là chỉ có một thread có thể khóa (hoặc sở hữu) một biến mutex tại bất kỳ thời điểm nào. Vì vậy, ngay cả khi nếu một vài thread cố khóa một mutex thì chỉ một thread sẽ thành công. Không có thread khác có thể sở hữu mutex đó cho tới khi thread sở hữu mở khóa mutex đó. Những thread phải thay phiên nhau tuy cập dữ liệu được bảo vệ. Mutex có thể được sử dụng để ngăn chặn điều kiện tương tranh. Một ví dụ về điều kiện tương tranh liên quan đến một giao dịch ngân hàng được chỉ ra dưới đây: Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 27 Lớp : K51CHTTT Trong ví dụ trên, một mutex nên được sử dụng để khóa “balance” trong khi một thread đang sử dụng nguồn dữ liệu được chia sẻ này. Rất thường xuyên các hành động được thực hiện bởi một thread sở hữu một mutex là cập nhật các biến toàn cục. Đây là một cách an toàn để đảm bảo rằng khi một vài thread cập nhật cùng biến này, giá trị cuối cùng là giống như những gì nó sẽ được nếu chỉ có một thread thực hiện cập nhật. Các biến đang được cập nhật thuộc về “critical section”. Một trình tự thông thường trong việc sử dụng một mutex là như sau: [1] Tạo ra và khởi tạo một biến mutex. [2] Một vài thread thử khóa mutex. [3] Chỉ một thread thành công và thread đó sỡ hữu mutex. [4] Thread sở hữu thực hiện một vài tập các hành động. [5] Thread sở hữu mở khóa mutex. [6] Một thread khác giành được mutex và lặp lại quá trình trên. [7] Cuối cùng mutex bị phá hủy. Khi bảo vệ dữ liệu được chia sẻ, đó là trách nhiệm của người lập trình để đảm bảo mọi thread có nhu cầu sử dụng một mutex. Ví dụ, nếu bốn thread đang được cập nhật cùng dữ liệu, nhưng chỉ có một sử dụng mutex, dữ liệu vẫn có thể bị hỏng. 2.3.2. Tạo ra và phá hủy mutex 2.3.2.1. Những thủ tục ƒ Pthread_mutex_init(mutex, attr) ƒ Pthread_mutex_destroy(mutex) Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 28 Lớp : K51CHTTT ƒ Pthead_mutexattr_init (attr) ƒ Pthread_mutexattr_destroy(attr) 2.3.2.2. Cách sử dụng Biến mutex phải được khia bảo với kiểu pthread_mutex_t và phải được khởi tạo trước khi nó được sử dụng. Có hai cách để khởi tạo một biến mutex: ƒ Theo cách tĩnh, khi nó được khai báo. Ví dụ: Pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZE. ƒ Theo cách động, với thủ tục pthread_mutex_init(). Phương thức này cho phép thiết lập đối tượng thuộc tính mutex, attr. Đối tương attr được sử dụng để thiết lập thuộc tính cho đối tượng mutex và phải là kiểu pthead_mutexattr_t nếu được sử dụng (có thế để là NULL nếu thiết lập mặc định). Chuẩn Pthread định nghĩa 3 thuộc tính tùy chọn cho mutex: ƒ Protocol: Chỉ định giao thức được sử dụng để ngăn chặn sự đảo lộn ưu tiên cho một mutex. ƒ Prioceiling: chỉ định trần ưu tiên của một mutex. ƒ Process-shared: chỉ định quá trình chia sẻ một mutex. Chú ý rằng không phải tất cả các thực hiện có thể cung cấp ba thuộc tính mutex tùy chọn. Các thủ tục pthead_mutexattr_init() và pthread_mutexattr_destroy() được dùng để tạo ra và phá hủy đối tượng thuộc tính mutex tương ứng. Pthead_mutexattr_destroy() nên được dùng để giải phóng đối tượng mutex khi nó không còn cần thiết. 2.3.3. Khóa và mở khóa mutex 2.3.3.1. Các thủ tục ƒ Pthread_mutex_lock(mutex) ƒ Pthead_mutex_trylock(mutex) ƒ Pthead_mutex_unlock(mutex) 2.3.3.2. Cách sử dụng Thủ tục pthread_mutex_lock() được sử dụng bởi một thread để thu được một khóa trên một biến mutex xác định. Nếu biến mutex đã được khóa bởi một thread khác, lời gọi này sẽ chặn lời gọi thread cho tới khi mutex được mở khóa. Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 29 Lớp : K51CHTTT Thủ tục pthread_mutex_trylock() sẽ cố thử khóa một mutex. Tuy nhiên, nếu mutex đã được khóa, thủ tục này sẽ ngay lập tức trả về lỗi code “busy”. Thủ tục này có thể có ích trong việc ngăn ngừa điều kiện bế tắc, như trong hoàn cảnh ưu tiên đảo ngược ( priority-inversion ). Thủ tục pthread_mutex_unlock() sẽ mở khóa một mutex nếu được gọi bởi thread đang sở hữu mutex đó. Việc gọi thủ tục này được yêu cầu sau khi một thread hoàn thành việc sử dụng dữ liệu được bảo vệ nếu thread khác giành được mutex cho công việc của chúng với dữ liệu được bảo vệ. Một lỗi sẽ được trả về nếu: ƒ Mutex này đã được mở khóa. ƒ Mutex được sở hữu bởi thread khác. Không có gì gọi là “ma thuật” về mutexes… trong thực tế, chúng na ná như một “thỏa thuận của quý ông (gentlement’s agreement)” giữa các thread tham gia. Đây là thiết lập của người viết chương trình để đảm bảo rằng những thread cần thiết làm cho các mutex khóa và mở khóa một cách chính xác. Kịch bản sau đây thể hiện một lỗi logic: Ví dụ sau đây minh họa cho cách sử dụng một biến mutex trong một chương trình thread . Dữ liệu chính luôn sẵn sàng cho tất cả các thread thông qua một biến struct toàn cục. Mỗi thread làm việc trên một phần dữ liệu khác nhau. Thread chính đợi cho đến khi tất cả các thread khác hoàn thành xong việc tính toán của chúng, và sau đó in kết quả tổng: ********************************************************************* #include #include #include /* The following structure contains the necessary information Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 30 Lớp : K51CHTTT to allow the function "dotprod" to access its input data and place its output into the structure. */ typedef struct { double *a; double *b; double sum; int veclen; } DOTDATA; /* Define globally accessible variables and a mutex */ #define NUMTHRDS 4 #define VECLEN 100 DOTDATA dotstr; pthread_t callThd[NUMTHRDS]; pthread_mutex_t mutexsum; /* The function dotprod is activated when the thread is created. All input to this routine is obtained from a structure of type DOTDATA and all output from this function is written into this structure. The benefit of this approach is apparent for the multi-threaded program: when a thread is created we pass a single argument to the activated function - typically this argument is a thread number. All the other information required by the function is accessed from the globally accessible structure. */ void *dotprod(void *arg) { /* Define and use local variables for convenience */ Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 31 Lớp : K51CHTTT int i, start, end, len ; long offset; double mysum, *x, *y; offset = (long)arg; len = dotstr.veclen; start = offset*len; end = start + len; x = dotstr.a; y = dotstr.b; /* Perform the dot product and assign result to the appropriate variable in the structure. */ mysum = 0; for (i=start; i<end ; i++) { mysum += (x[i] * y[i]); } /* Lock a mutex prior to updating the value in the shared structure, and unlock it upon updating. */ pthread_mutex_lock (&mutexsum); dotstr.sum += mysum; pthread_mutex_unlock (&mutexsum); pthread_exit((void*) 0); } /* The main program creates threads which do all the work and then Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 32 Lớp : K51CHTTT print out result upon completion. Before creating the threads, the input data is created. Since all threads update a shared structure, we need a mutex for mutual exclusion. The main thread needs to wait for all threads to complete, it waits for each one of the threads. We specify a thread attribute value that allow the main thread to join with the threads it creates. Note also that we free up handles when they are no longer needed. */ int main (int argc, char *argv[]) { long i; double *a, *b; void *status; pthread_attr_t attr; /* Assign storage and initialize values */ a = (double*) malloc (NUMTHRDS*VECLEN*sizeof(double)); b = (double*) malloc (NUMTHRDS*VECLEN*sizeof(double)); for (i=0; i<VECLEN*NUMTHRDS; i++) { a[i]=1.0; b[i]=a[i]; } dotstr.veclen = VECLEN; dotstr.a = a; dotstr.b = b; dotstr.sum=0; pthread_mutex_init(&mutexsum, NULL); /* Create threads to perform the dotproduct */ pthread_attr_init(&attr); Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 33 Lớp : K51CHTTT pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); for(i=0; i<NUMTHRDS; i++) { /* Each thread works on a different set of data. The offset is specified by 'i'. The size of the data for each thread is indicated by VECLEN. */ pthread_create(&callThd[i], &attr, dotprod, (void *)i); } pthread_attr_destroy(&attr); /* Wait on the other threads */ for(i=0; i<NUMTHRDS; i++) { pthread_join(callThd[i], &status); } /* After joining, print out the results and cleanup */ printf ("Sum = %f \n", dotstr.sum); free (a); free (b); pthread_mutex_destroy(&mutexsum); pthread_exit(NULL); } 2.4. Biến điều kiện 2.4.1. Khái niệm về biến điều kiện Biến điều kiện cung cấp một cách nữa cho thread để đồng bộ hóa. Trong khi mutexes thể hiện sự đồng bộ bằng cách điều khiển thread truy cập tới dữ liệu thì biến điều kiện cho phép thread đồng bộ dựa trên giá trị thực của dữ liệu. Nếu không có biến điều kiện, người lập trình sẽ cần phải có thread polling một cách liên tục (có Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 34 Lớp : K51CHTTT thể trong một phần quan trọng), để kiểm tra xem nếu điều kiện được đáp ứng. Điều này có thể rất tốn tài nguyên kể từ khi thead bận rộn liên tục trong hoạt động này. Một biến điều kiện là một cách để đạt được cùng một mục tiêu mà không cần polling. Một biến điều kiện sẽ luôn được sử dụng cùng với một khóa mutex. Một chuỗi đại diện cho việc sử dụng các biến điều kiện được hiển thị trong bảng sau: Thread chính: - Khai báo và khởi tạo dữ liệu/biến toàn cục cần đồng bộ hóa (ví dụ “count”). - Khai bảo và khởi tạo biến điều kiện. - Khai bảo và khởi tạo mutex liên quan. - Tạo thread A và B để làm việc Thread A - Làm việc tới khi một điều kiện nhất định phải xảy ra.(ví dụ khi biến count đạt được một giá trị cụ thể nào đấy) - Khóa mutex liên quan và kiểm tra giá trị của biến toàn cục. - Gọi thủ tục pthead_cond_wait() để thực hiện việc ngăn chặn đợi tín hiệu từ thread B. Chú ý rằng, một lời gọi tới hàm pthead_cond_wait() sẽ tự động mở khóa cho mutex liên quan để nó có thể được sử dụng bởi thread B. - Khi đã nhận được tín hiệu,thread A sẽ “wake up”. Mutex sẽ tự động bị khóa. - Mở khóa mutex. Thread B - Làm việc - Khóa mutex liên quan. - Thay đổi giá trị của biến toàn cụng để thread A chờ. - Kiểm tra giá trị của biến toàn cục mà thread A chờ. Nếu nó thỏa mãn điều kiện mong muốn, tín hiệu cho thread A. - Mở khóa mutex. - Tiếp tục Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 35 Lớp : K51CHTTT - Tiếp tục. Thread chính: - Nối / Tiếp tục 2.4.2. Tạo ra và phá hủy 1 biến điều kiện 2.4.2.1. Các thủ tục ƒ Pthread_cond_init (condition, attr) ƒ Pthread_cond_destroy(condition) ƒ Pthead_condattr_init(attr) ƒ Pthread_condattr_destroy(attr) 2.4.2.2. Cách sử dụng Biến điều kiện phải được khai báo với kiểu pthread_cond_t, và phải được khởi tạo trước khi chúng được sử dụng. Có hai cách để khởi tạo biến điều kiện: ƒ Theo cách tĩnh, khi chúng được khai báo: pthread_cond_t myconvar = PTHREAD_COND_INITIALIZER; ƒ Theo cách động, với thủ tục pthread_cond_init. ID của biến điều kiện được tao ra được trả về với lời gọi thread thông qua tham số condition. Phương thức này cho phép thiết lập thuộc tính cho biến điều kiện, attr. Đối tượng tùy chọn attr được sử dụng để thiết lập thuộc tính cho biến điều kiện. Chỉ có một thuộc tính được định nghĩa cho biến điều kiện là process-shared, cho phép biến điều kiện được nhìn bởi các thread trong tiến trình khác. Đối tượng thuộc tính, nếu được sử dụng, phải có kiểu pthread_condattr_t (để là NULL nếu chấp nhận thuộc tính mặc định). Chú ý rằng không phải tất cả các thể hiện đểu có thể cung cấp thuộc tính này. Các thủ tục pthead_condattr_init() và pthread_condattr_destroy() được sử dụng để tạo ra và phá hủy đối tượng thuộc tính của biến điều kiện. Thủ tục pthread_cond_destroy() nên được sử dụng để giải phóng biến điều kiện nếu nó không còn cần thiết nữa. 2.4.3. Waiting và signaling trên biến điều kiện Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 36 Lớp : K51CHTTT 2.4.3.1. Các thủ tục ƒ Pthread_cond_wait (condition, mutex) ƒ Pthread_cond_signal(condition) ƒ Pthread_cond_broadcast(condition) 2.4.3.2. Cách sử dụng Thủ tục pthread_cond_wait() ngăn chắn lời gọi thread cho tới khi điểu kiện (condition) xác định được báo hiệu. Thủ tục này nên được gọi trong khi mutex bị khóa và nó sẽ tự động mở khóa mutex trong khi chờ đợi. Sau khi tín hiệu nhận được và thread tiếp tục, mutex sẽ tự động bị khóa để sử dụng bởi thread. Người lập trình sau đó chịu trách nhiệm mở khóa khi thread hoàn thành với nó. Thủ tục pthead_cond_singal() được dùng để báo hiệu (hoặc đánh thức) thread khác đang đợi trên biến điều kiện. Nó nên được gọi sau khi biến mutex bị khóa, và phải mởi khóa mutex theo thứ tự cho thủ tục pthread_cond_wait() được hoàn thành. Thủ tục pthread_cond_broadcast() nên được sử dụng thay vì pthread_cond_signal () nếu có nhiều hơn một thread đang ở trong trạng thái chờ. Sẽ có một lỗi logic nêu gọi pthread_cond_signal trước khi gọi pthread_cond_wait(). Ví dụ đơn giản để làm rõ cách sử dụng của một vài biến điều kiện Pthread. Trong hàm main() tạo ra ba thread. Hai trong số đó thực hiện việc và cập nhật biến. Thread thứ ba đợi cho đến khi biến count nhận được giá trị xác định. #include #include #include #define NUM_THREADS 3 #define TCOUNT 10 #define COUNT_LIMIT 12 int count = 0; int thread_ids[3] = {0,1,2}; pthread_mutex_t count_mutex; Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 37 Lớp : K51CHTTT pthread_cond_t count_threshold_cv; void *inc_count(void *t) { int i; long my_id = (long)t; for (i=0; i<TCOUNT; i++) { pthread_mutex_lock(&count_mutex); count++; /* Check the value of count and signal waiting thread when condition is reached. Note that this occurs while mutex is locked. */ if (count == COUNT_LIMIT) { pthread_cond_signal(&count_threshold_cv); printf("inc_count(): thread %ld, count = %d Threshold reached.\n", my_id, count); } printf("inc_count(): thread %ld, count = %d, unlocking mutex\n", my_id, count); pthread_mutex_unlock(&count_mutex); /* Do some "work" so threads can alternate on mutex lock */ sleep(1); } pthread_exit(NULL); } void *watch_count(void *t) { long my_id = (long)t; printf("Starting watch_count(): thread %ld\n", my_id); /* Khóa luận tốt nghiệp Nghiên cứu lập trình thread và ứng dụng Sinh viên: Cấn Việt Dũng 38 Lớp : K51CHTTT Lock mutex and wait for

Các file đính kèm theo tài liệu này:

  • pdfNghiên cứu lập trình thread và ứng dụng.pdf