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
60 trang |
Chia sẻ: oanh_nt | Lượt xem: 3530 | Lượt tải: 4
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:
- Nghiên cứu lập trình thread và ứng dụng.pdf