LỜI CẢM ƠN
Lời đầu tiên tôi xin được gửi lời cảm ơn chân thành tới các thầy cô giáo Trường Đại
Học Công Nghệ - Đại Học Quốc Gia Hà Nội, đặc biệt là các thầy cô trong khoa Công
Nghệ Thông Tin, những người đã trực tiếp chỉ bảo tôi những kiến thức trong suốt bốn
năm học vừa qua trên ghế giảng đường.
Đặc biệt tôi xin được bày tỏ lòng kính trọng và biết ơn tới TS. Nguyễn Hải Châu
người đã trực tiếp hướng dẫn, giúp đỡ tôi hoàn thành khóa luận này.
Xin được gửi lời chúc sức khỏe và hạnh phúc tới tất cả các thầy cô. Xin chúc thầy cô
đạt được nhiều thành tựu hơn nữa trong sự nghiệp đào tạo tri thức cho đất nước cũng như
trong các công việc nghiên cứu khoa học.
Trân trọng cảm ơn!
Hà Nội, Ngày 20 Tháng 05 năm 2010
Sinh viên thực hiện
Cấn Việt Dũng
TÓM TẮT KHÓA LUẬN
Trong khóa luận này, tôi sẽ trình bày lý thuyết về Thread, lập trình posix thread
và cài đặt một bài toán sử dụng posix thread để thấy được hiệu quả của việc sử dụng
chúng. Phần đầu tiên,tôi giới thiêu lý thuyết về Thread, Multi thread và các vấn đề
liên quan. Tiếp theo, tôi trình bày về lập trình posix thread trên hệ điều hành linux,
bao gồm cách tạo, ngắt thread và đồng bộ các thread trong chương trình. Cuối cùng,
là cách cài đặt bài toán “tìm cặp điểm gần nhau nhất trong tập N điểm cho trước”( qui
ước bài toán này là bài toán closest_pair) sử dụng posix thread trên ngôn ngữ C để
thấy được sự hiệu quả của việc sử dụng multithread trong việc nâng cao hiệu năng
của chương trình, ở đây cụ thể là tốc độ tính toán tăng lên rõ rệt.
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
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
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_PAIR TRONG 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
GIỚI THIỆU
Thread là một mô hình lập trình phổ biến cho phép nhiều thread đơn có thể
chạy trên cùng một tiến trình, và các thread này có thể chia sẻ tài nguyên của tiến
trình cũng như có thể tính toán độc lập. Và ứng dụng hữu ích nhất của mô hình này
là khi nó được áp dụng cho một tiến trình đơn lẻ để cho phép tính toán song song
trên một hệ thống đa xử lý. Trong khóa luận này, tôi sẽ trình bày mô hình này trên
chuẩn IEEE POSIX 1003.1c, được gọi là POSIX thread hay Pthread. Lý do tôi chọn
Pthread, là để nhận ra hiệu quả tiềm năng của chương trình, việc tạo ra một thread
sử dụng ít tài nguyên và chi phí của hệ điều hành hơn rất nhiều so với việc tạo ra
một tiến trình.
Nội dung chính của khóa luận bao gồm 3 chương, nội dung cụ thể như sau:
Chương I: Giới thiệu về thread và multi thread. Chương này tập trung giới thiệu
về thread và multi thread, so sánh giữa thread với tiến trình và cùng với đó là những
lợi thế khi sử dụng multi thread. Cuối cùng là các mô hình thread và các ngôn ngữ
hỗ trợ.
Chương II: Lập trình POSIX thread. Chương này sẽ đề cập tới các vấn đề cơ bản
trong lập trình POSIX thread (Pthread). Các vấn đề được đề cập bao gồm việc quản
lý thread, tạo, hủy, tách và nối thread. Các biến mutex, biến điều kiện và cách sử
dụng. Mỗi phần đều có những ví dụ minh họa.
Chương III: Bài toán closest_pair. Chương này tôi sẽ cài đặt bài toán closest_pair
hai chiều bằng các phương pháp thông thường, đệ qui và đệ qui sử dụng multi thread
để thấy được hiệu quả của việc sử dụng multi thread.
60 trang |
Chia sẻ: lvcdongnoi | Lượt xem: 4074 | Lượt tải: 2
Bạn đang xem trước 20 trang tài liệu Đề tài 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
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 signal. Note that the pthread_cond_wait
routine will automatically and atomically unlock mutex while it waits.
Also, note that if COUNT_LIMIT is reached before this routine is run by
the waiting thread, the loop will be skipped to prevent pthread_cond_wait
from never returning.
*/
pthread_mutex_lock(&count_mutex);
if (count<COUNT_LIMIT) {
pthread_cond_wait(&count_threshold_cv, &count_mutex);
printf("watch_count(): thread %ld Condition signal received.\n", my_id);
count += 125;
printf("watch_count(): thread %ld count now = %d.\n", my_id, count);
}
pthread_mutex_unlock(&count_mutex);
pthread_exit(NULL);
}
int main (int argc, char *argv[])
{
int i, rc;
long t1=1, t2=2, t3=3;
pthread_t threads[3];
pthread_attr_t attr;
/* Initialize mutex and condition variable objects */
pthread_mutex_init(&count_mutex, NULL);
pthread_cond_init (&count_threshold_cv, NULL);
/* For portability, explicitly create threads in a joinable state */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[0], &attr, watch_count, (void *)t1);
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 39 Lớp : K51CHTTT
pthread_create(&threads[1], &attr, inc_count, (void *)t2);
pthread_create(&threads[2], &attr, inc_count, (void *)t3);
/* Wait for all threads to complete */
for (i=0; i<NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
printf ("Main(): Waited on %d threads. Done.\n", NUM_THREADS);
/* Clean up and exit */
pthread_attr_destroy(&attr);
pthread_mutex_destroy(&count_mutex);
pthread_cond_destroy(&count_threshold_cv);
pthread_exit(NULL);
}
2.5. Dữ liệu riêng của Thread(Thread – specific data)
2.5.1. Khái niệm dữ liệu riêng của thread
Trong cơ chế dữ liệu thread cụ thể, chúng ta có khái niệm về các khóa và giá
trị. Mỗi khóa có một tên, và trỏ tới một vài vùng nhớ. Những khóa với cùng tên
trong hai thread đồng thời luôn luôn trỏ tới những vùng nhớ khác nhau, điều này
được quản lý bởi thư viện các hàm để bố trí các khối bộ nhớ để truy cập thông qua
các khóa này. Chúng ta có một hàm để tạo ra khóa (được gọi một lần mỗi tên khóa
trong toàn bộ quá trình), một hàm để cấp phát bộ nhớ (được gọi đồng thời trong mỗi
thread), các hàm để ngừng cấp phát bộ nhớ cho các thread cụ thể và một hàm để phá
hủy khóa. Chúng ta cũng có những hàm để truy cập tới dữ liệu được trỏ bởi một
khóa, cũng như thiết lập giá trị, hoặc trả về giá trị mà nó trỏ tới.
2.5.2. Cấp phát dữ liệu riêng của thread
Hàm pthread_key_create() được dùng để cấp phát một khóa mới. Khóa này
bây giờ sẽ trở nên có giá trị cho tất cả các thread trong tiến trình. Khi một khóa được
tạo ra, giá trị mặc định là NULL. Sau đó trong mỗi thread có thể thay đổi giá trị như
nó mong muốn. Sau đây là cách sử dụng của hàm nà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 40 Lớp : K51CHTTT
/* rc được dùng để lưu giá trị trả về của hàm pthread_key_create() */
int rc;
/* định nghĩa 1 biến để nắm giữ khóa, tạo ra 1 lần */
pthread_key_t list_key;
/* cleanup_list là một hàm có thể làm sạch dữ liệu */
extern void* cleanup_list(void*);
/* Tạo ra khóa */
rc = pthread_key_create(&list_key, cleanup_list);
Chú ý sau khi hàm pthread_key_create() trả về, biến list_key sẽ trỏ tới cái
khóa vừa mới được tạo ra. Con trỏ hàm trong tham số thứ hai của hàm
pthread_key_create() sẽ tự động được gọi bởi thư viện Pthread khi thoát ra khỏi
thread, với một con trỏ trỏ đến giá trị của khóa như là một tham số. Chúng ta cũng
có thể dùng con trỏ NULL và khi đó sẽ không có hàm nào được trả về cho khóa.
Chú ý rằng, các hàm được gọi một lần trong mỗi thread, kể cả khi chúng ta tạo ra
khóa này chỉ một lần, trong một thread. Nếu chúng ta tạo ra vài khóa, hàm
destructor liên quan sẽ được gọi theo một trật tự tùy ý, bất kể thứ tự của các khóa tạo
ra.
Nếu hàm pthread_key_create() thành công, nó sẽ trả về giá trị 0. Ngược lại, nó
sẽ trả về vài dòng lỗi. Có một giới hạn PTHREAD_KEYS_MAX() có thể tồn tại
trong quá trình tại bất kỳ thời điểm nào. Mọi cố gắng để tạo ra khóa sau khi thoát
PTHREAD_KEYS_MAX sẽ gặp phải một giá trị trả về là EAGAIN từ hàm
pthread_key_create().
2.5.3. Truy cập vào dữ liệu riêng của thread
Sau khi tạo ra một khóa, chúng ta có thể truy cập vào giá trị của chúng bằng
cách sử dụng hai hàm: pthread_getspecific() và pthread_setspecific() . Hàm đầu tiên
được dùng để lấy giá trị của một khóa được đưa ra còn hàm sau được dùng để thiết
lập giá trị của khóa được đưa ra. Giá trị của một khóa chỉ là một con trỏ kiểu void
(void*), do đó chúng ta có thể lưu bất cứ thứ gì chúng ta muốn. Hãy xem cách sử
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 41 Lớp : K51CHTTT
dụng của những hàm này. Giả sử rằng ‘a_key’ là một biến khởi tạo kiểu
pthread_key_t để chứa khóa được tạo ra trước:
/* biến này được sử dụng để lưu giá trị trả về của hàm */
int rc;
/* định nghĩa một biến mà chúng ta sẽ lưu vào đó một vài dữ liệu, ví dụ là 1 số
nguyên */
int* p_num = (int*)malloc(sizeof(int));
if (!p_num) {
fprintf(stderr, "malloc: out of memory\n";
exit(1);
}
/* khởi tạo giá trị */
(*p_num) = 4;
/* bây giờ lưu giá trị này vào khóa */
/* chú ý rằng chúng ta không lưu ‘p_num’ vào khóa */
/* chúng ta chỉ lưu giá trị mà p_num trỏ tới */
rc = pthread_setspecific(a_key, (void*)p_num);
.
/* lấy giá trị của khóa a_key và in ra */
{
int* p_keyval = (int*)pthread_getspecific(a_key);
if (p_keyval != 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 42 Lớp : K51CHTTT
printf("value of 'a_key' is: %d\n", *p_keyval);
}
}
Chú ý rằng, nếu chúng ta thiết lập giá trị cho khóa trong một thread, và cố thử
lấy giá trị trong thread khác, chúng ta sẽ nhận được giá trị NULL, giá trị này là khác
biệt cho mỗi thread. Cũng có hai trường hợp mà hàm pthread_getspecific() trả về
NULL:
- Khóa được cung cấp như tham số không hợp lệ ( ví dụ như khóa chưa được
tạo).
- Giá trị của khóa là NULL. Điều này có thể là nó chưa được khởi tạo, hoặc
được đặt là NULL bởi lời gọi pthread_setspecific() trước.
2.5.4. Xóa dữ liệu trong thread
Hàm pthread_key_delete() có thể được dùng để xóa khóa. Nhưng không được
nhầm lẫn với tên của hàm này: nó không xóa được vùng nhớ liên quan tới khóa,
cũng không gọi hàm hủy được định nghĩa trong khi khóa được tạo ra. Vì vậy, vẫn
cần giải phóng vùng nhớ nếu cần thiết.
Sử dụng hàm này rất đơn giản. Giải sử list_key là một biến kiểu pthread_key_t
trỏ tới một khóa đã được tạo, sử dụng hàm này như sau:
Int rc = pthread_key_delete(key);
Hàm này sẽ trả về 0 nếu thành công, hoặc EINVAL nếu có lỗi.
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 43 Lớp : K51CHTTT
CHƯƠNG 3: BÀI TOÁN CLOSEST_PAIR TRONG KHÔNG GIAN HAI
CHIỀU SỬ DỤNG MULTITHREADING
3.1. Giới thiệu bài toán
Cho trước N điểm trong hệ tọa độ Đề-các hai chiều. Tìm cặp điểm gần nhau nhất
trong N điểm nói trên. (Trong khóa luận này, tôi mới chỉ ra được khoảng cách ngắn nhất
giữa hai điểm trong N điểm trên nhưng chưa chỉ ra được cặp điểm đó).
Để giải quyết bài toán này, có 2 thuật toán được đưa ra. Thuật toán thứ nhất, đó là ta
đi duyệt tất cả các cặp điểm trong N điểm trên, so sanh khoảng cách rồi chọn ra hai điểm
gần nhau nhất. Thuật toán này khá đơn giản, nhưng có độ phức tạp lớn: O(n2). Thuật toán
thứ hai, còn được gọi là thuật toán chia để trị. Ý tưởng của thuật toán này là, ta chia N
điểm cho trước thành hai nửa, trái và phải, với số điểm tương đương nhau rồi ta tính toán
đệ qui khoảng cách ngắn nhất trên mỗi nửa đó. So với thuật toán thứ nhất, thuật toán này
có độ phức tạp nhỏ hơn rất nhiểu: O(nlogn).
Cùng với cách cài đặt hai thuật toán trên, trong khóa luận tốt nghiệp này tôi đã cài
đặt song song hóa thuật toán thứ hai để làm tăng hiệu năng tính toán. Cách song song hóa
thuật toán là, sau khi chia tập N điểm thành hai phần trái và phải, tôi tạo một thread để
tính toán nửa bên trái, như vậy, cùng với thread chính(của hàm main()), sẽ có một thread
tính toán nửa bên trái chạy song song cùng với nó. Điều này làm tăng đáng kể hiệu năng
tính toán của chương trình. Cụ thể các cách cài đặt được nêu rõ ở phần tiếp theo.
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.
- Thuật toán 1: ta duyệt tất cả các cặp điểm trong N điểm cho trước, so sánh khoảng
cách giữa các cặp điểm đó rồi chọn ra khoảng cách ngắn nhất. Thuật toán này có
độ phức tạp tính toán O(n2).
Mã lệnh thực hiện thuật toán:
#include
#include
#include
#define N 8000
struct coordinate{
float x;
float 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 44 Lớp : K51CHTTT
/*---------------------------------------------------------
---------------------
Ham tinh khoang cach giua 2 diem
-----------------------------------------------------------
-------------------*/
float distance(struct coordinate point1, struct
coordinate point2){
float temp1, temp2;
temp1 = point2.x - point1.x;
temp2 = point2.y - point1.y;
return sqrt(temp1*temp1 +
temp2*temp2);
};
/*---------------------------------------------------------
---------------------
Ham tim gia tri nho nhat
-----------------------------------------------------------
-------------------*/
float min(float a, float b)
{
if (a < b)
return a;
else
return b;
};
/*---------------------------------------------------------
---------------------
Ham truyen gia tri cho cac diem
-----------------------------------------------------------
-------------------*/
void input(struct coordinate point[])
{
int i = 0;
float temp1,temp2;
FILE *fp;
if((fp=fopen("input.txt","r")) == NULL)
{
printf("Cannot open file.\n");
}
while(fscanf(fp, "%f %f", &point[i].x,
&point[i].y) != eof())
i++;
};
/*---------------------------------------------------------
--------------------
Ham chinh
-----------------------------------------------------------
-------------------*/
int main()
{
int i,j;
float result,temp;
struct coordinate point[N],point2[2];
input(point);
result = distance(point[0],point[1]);
point2[0] = point[0];
point2[1] = point[1];
for(i = 0; i < N; i++)
for(j = i + 1; j < N; j++)
{
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 45 Lớp : K51CHTTT
temp = distance(point[i],point[j]);
if(temp < result){
point2[0] = point[i];
point2[1] = point[j];
result = temp;
}
}
printf("Ket qua la :%2.2f",result);
printf("\nCap diem la: (%2.2f,%2.2f)
va (%2.2f,%2.2f)",
point2[0].x,point2[0].y,point2[1].x,
point2[1].y);
}
- Thuật toán 2: ta thực hiện bài toán theo phương pháp chia để trị.
Bước 1: Sắp xếp tăng dần tập N điểm cho trước theo tọa độ x. Ở đây tác giả
khóa luận sử dụng thuật toán quicksort để sắp xếp.
Bước 2: Gọi tập N điểm cho trước là tập S. Ta chia S thành hai tập con S1
và S2 (S1 gọi là nửa trái, S2 gọi là nửa phải) sao cho số điểm trong mỗi tập
là tương đương nhau bằng đường thẳng L. Đường thẳng L được xác định
bằng phương trình x = x_mid, với x_mid là tọa độ điểm chính giữa trong tập
S.
Bước 3: Thực hiện tính toán đệ qui closest_pair đối với hai nửa trái và phải.
Giả sử tìm được khoảng cách ngắn nhất ở nửa trái là dL và ở nửa phải là dR.
Bước 4: Tìm khoảng cách ngắn nhất delta giữa hai cặp điểm mà trong đó
một điểm nằm bên trái đường thẳng L và một điểm nằm bên phải đường
thằng L. Phần này là phần khó nhất trong bài toán. Giả sử ∂ = min(dL,dR).
Ta chọn tất cả những điểm ở S1 và S2 mà khoảng cách từ điểm đó tới
đường thẳng L nhỏ hơn hoặc bằng ∂. Ta lưu những điểm thỏa mãn điều kiện
đó trong tập S1 vào mảng tempL[], trong tập S2 vào mảng tempR[]. Bây giờ
ta đi tìm khoảng cách ngắn nhất giữa 2 điểm, với điều kiện trong 2 điểm
này, 1 điểm thuộc tempL[] và 1 điểm thuộc tempR[]. Cách thực hiện là, với
mỗi điểm p thuộc tempL[], ta chỉ xét những điểm ở tempR[] nằm trong hình
chữ nhật có kích thước (∂,2*∂) với p là điểm góc trên trái của hình chữ
nhật. Ta có thể chỉ ra rằng nhiều nhất là có 6 điểm thuộc tempR[] thỏa mãn
điều kiện này.
Kết quả cuối cùng là giá trị nhỏ nhất của dL,dR và delta.
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 46 Lớp : K51CHTTT
Khi thực hiện cài đặt bằng phương pháp này, độ phức tạp tính toán của thuật
toán là O(nlogn).
Mã lệnh thực hiện thuật toán:
#include
#include
#include
#include
#include
#include
#define N 8000
/*---------------------------------------
Struct luu toa do (x,y) cua 1
diem
---------------------------------------*/
struct coordinate{
float x;
float y;
};
/*---------------------------------------
Struct luu tham so truyen vao
cho ham closest_pair
---------------------------------------*/
struct thamso{
int left ;
int right ;
struct coordinate point[N];
};
/*---------------------------------------
Ham truyen gia tri cho cac
diem
---------------------------------------*/
void input(struct coordinate point[])
{
int i = 0;
char s[81];
FILE *fp;
fp = fopen("input.txt","r");
if(fp == NULL)
{
printf("Cannot open file.\n");
}
while(fgets(s,80,fp)!= NULL)
{
sscanf(s,"%f %f"
,&point[i].x,&point[i].y);
i++;
}
};
/*--------------------------------------Ham
lay thoi gian hien thoi toi mili
giay
---------------------------------------*/
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 47 Lớp : K51CHTTT
double gettime()
{
struct timeval x;
gettimeofday(&x, NULL);
double t = (double)(x.tv_sec +
x.tv_usec / 1000000.0);
return (t);
};
/*---------------------------------------
Ham tinh khoang cach giua 2
diem
--------------------------------------*/
float distance(struct coordinate
point1, struct coordinate
point2){
float temp1,temp2;
temp1 = point2.x - point1.x;
temp2 = point2.y - point1.y;
return sqrt(temp1*temp1 +
temp2*temp2);
};
/*---------------------------------------
Ham tinh min cua 2 gia tri
--------------------------------------*/
float min(float a, float b)
{
if (a < b) return a;
else return b;
};
/*---------------------------------------
Ham sap xep cac diem tang
dan theo toa do x
--------------------------------------*/
void quicksortX(struct coordinate
arr[], int low, int high) {
int i = low;
int j = high;
float x = 0.0;
float y = 0.0;
float z = arr[(i + j) / 2].x;
do {
while(arr[i].x < z) i++;
while(arr[j].x > z) j--;
if(i <= j) {
x = arr[i].x;
y = arr[i].y;
arr[i].x = arr[j].x;
arr[i].y = arr[j].y;
arr[j].x = x;
arr[j].y = y;
i++;
j--;
}
} while(i <= j);
if(low < j)
quicksortX(arr, low, j);
if(i < high)
quicksortX(arr, i, high);
};
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 48 Lớp : K51CHTTT
/*---------------------------------------
Ham sap xep cac diem tang
dan theo toa do y
--------------------------------------*/
void quicksortY(struct coordinate
arr[], int low, int high) {
int i = low;
int j = high;
float x = 0.0;
float y = 0.0;
float z = arr[(low + high) / 2].y;
do {
while(arr[i].y < z) i++;
while(arr[j].y > z) j--;
if(i <= j) {
x = arr[i].x;
y = arr[i].y;
arr[i].x = arr[j].x;
arr[i].y = arr[j].y;
arr[j].x = x;
arr[j].y = y;
i++;
j--;
}
} while(i <= j);
if(low < j)
quicksortY(arr, low, j);
if(i < high)
quicksortY(arr, i, high);
};
/*-------------------------------------*/
void sortX(struct coordinate
toado[], int n)
{
quicksortX(toado,0,n-1);
};
/*---------------------------------*/
void sortY(struct coordinate
toado[], int n)
{
quicksortY(toado,0,n-1);
};
/*---------------------------------------
Tao 1 stack luu gia tri tra ve
cua ham closest_pair
Ham push() day 1 gia tri vao
stack
Ham pop() lay gia tri ra khi
stack
--------------------------------------*/
float stack[100];
int ip=-1;
void push(float v) {
ip++;
stack[ip] = v;
return;
}
float pop() {
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 49 Lớp : K51CHTTT
if (ip >= 0) {
float v = stack[ip];
ip--;
return v;
}
else {
printf("error: ip = %d\n", ip);
return -11;
}
}
/*---------------------------------------
Ham tinh toan khoang cach
ngan nhat
--------------------------------------*/
void closest_pair(void* args)
{
struct thamso* temp = (struct
thamso*) args;
int i,j,mid, countL = 0,countR
= 0,tempLeft,tempRight;
float
midX,dL,dR,temp1,delta;
struct coordinate
tempL[N],tempR[N];
tempLeft = temp->left;
tempRight = temp->right;
mid = (temp->left + temp-
>right)/2.0;
midX = temp->point[mid].x;
if((temp->right - temp->left)
== 1 )
{
push(distance(temp-
>point[temp->right],temp-
>point[temp->left]));
return;
}
//de qui ben trai,gan lai left
=0, right = mid
temp->left = tempLeft;
temp->right = mid;
closest_pair(temp);
dL = pop();
//de qui ben phai,gan lai left
= mid, right = N -1
temp->left = mid;
temp->right = tempRight;
closest_pair(temp);
dR = pop();
delta = min(dL,dR);
// Lay ra nhung phan tu nam
trong khoang delta ben trai
for(i = tempLeft; i < mid; i++)
{
if(abs(temp->point[i].x -
midX) <= delta)
{
tempL[countL] = temp-
>point[i];
countL ++;
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 50 Lớp : K51CHTTT
}
}
// Lay ra nhung phan tu nam
trong khoang delta ben phai
for (i = mid; i < tempRight;
i++)
{
if(abs(temp->point[i].x -
midX) <= delta)
{
tempR[countR] = temp-
>point[i];
countR ++;
}
}
// Sap xep nhung phan tu
trong khoang delta theo toa
do Y
sortY(tempL,countL);
sortY(tempR,countR);
temp1 = delta;
// Kiem tra xem trong khoang
delta co ton tai cap diem nao
co khoang cach nho hon
delta hay khong
for(i = 0; i < countL; i ++)
{
j = 0;
while((j < countR) &&
(abs(tempR[j].y - tempL[i].y)
<= 2*delta))
{
j++;
temp1 =
min(temp1,distance(tempL[i],
tempR[j]));
}
}
delta = min(temp1,delta);
push(delta);
return ;
};
/*---------------------------------------
Ham chinh
--------------------------------------*/
int main(){
float result;
double time1,time2;
struct thamso temp;
temp.left = 0;
temp.right = N-1;
input(&temp);
time1 = gettime();
sortX(temp.point,N);
closest_pair_top(&temp);
result = pop();
time2 = gettime();
printf("Thoi gian chay:
%lf\n",time2 - time1);
printf("khoang cach ngan
nhat la : %2.2f \n",result);
return 0;
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 51 Lớp : K51CHTTT
}
- Song song hóa thuật toán 2 sử dụng pthread để làm tăng hiệu năng tính toán. Cụ
thể ở đây, thời gian thực hiện giảm đi khoảng một nửa so với thuật toán 2.
Các vấn đề đặt ra khi cài đặt bài toán theo phương pháp này:
Cách tạo thread và truyền tham số cho thread. Với hàm closest_pair để tính
đệ qui, ta tạo một thread để tính toán đệ qui nửa bên trái chạy song song với
thread chính. Ta tạo một struct thamso lưu các giá trị left,right và tập các
điểm để truyền vào hàm closest_pair.
Khi nào thì join các thread. Ta phải join thread trên trước khi lấy ra giá trị
trả về của hàm closest_pair. Cụ thể ở đây, ta phải join thread trên trước khi
lấy giá trị ra khỏi ngăn xếp.
Điều kiện tương tranh. Ta phải tạo một biến mutex để tránh xảy ra điều
kiện tương tranh, vì có thể hai thread cùng tác động tơi biến đếm trong
stack. Cụ thể ở đây, ta phải khóa biến mutex ở đầu hàm push và pop và mở
khóa ở cuối mỗi hàm.
Mã lệnh của chương trình:
#include
#include
#include
#include
#include
#include
#define N 8000
/*-----------------------------------------------
Struct luu toa do (x,y) cua 1 diem
-----------------------------------------------*/
struct coordinate{
float x;
float y;
};
/*-----------------------------------------------
Struct luu tham so truyen vao cho ham
closest_pair
-----------------------------------------------*/
struct thamso{
int left ;
int right ;
struct coordinate point[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 52 Lớp : K51CHTTT
Ham truyen gia tri cho cac diem
-----------------------------------------------*/
void input(struct coordinate point[])
{
int i = 0;
char s[81];
FILE *fp;
fp = fopen("input.txt","r");
if(fp == NULL)
{
printf("Cannot open file.\n");
}
while(fgets(s,80,fp) != NULL)
{
sscanf(s,"%f %f",&point[i].x,&point[i].y);
i++;
}
};
/*-----------------------------------------------
Ham lay thoi gian hien thoi toi mili giay
-----------------------------------------------*/
double gettime()
{
struct timeval x;
gettimeofday(&x, NULL);
double t = (double)(x.tv_sec + x.tv_usec
/ 1000000.0);
return (t);
};
/*-----------------------------------------------
Ham tinh khoang cach giua 2 diem
-----------------------------------------------*/
float distance(struct coordinate point1, struct
coordinate point2){
float temp1,temp2;
temp1 = point2.x - point1.x;
temp2 = point2.y - point1.y;
return sqrt(temp1*temp1 + temp2*temp2);
};
/*-----------------------------------------------
Ham tinh min cua 2 gia tri
-----------------------------------------------*/
float min(float a, float b)
{
if (a < b) return a;
else return b;
};
/*-----------------------------------------------
Ham sap xep cac diem tang dan theo toa do
x
-----------------------------------------------*/
void quicksortX(struct coordinate arr[], int
low, int high) {
int i = low;
int j = high;
float x = 0.0;
float y = 0.0;
float z = arr[(i + j) / 2].x;
do {
while(arr[i].x < z) i++;
while(arr[j].x > z) j--;
if(i <= j) {
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 53 Lớp : K51CHTTT
x = arr[i].x;
y = arr[i].y;
arr[i].x = arr[j].x;
arr[i].y = arr[j].y;
arr[j].x = x;
arr[j].y = y;
i++;
j--;
}
} while(i <= j);
if(low < j)
quicksortX(arr, low, j);
if(i < high)
quicksortX(arr, i, high);
};
/*-----------------------------------------------
Ham sap xep cac diem tang dan theo toa do
y
-----------------------------------------------*/
void quicksortY(struct coordinate arr[], int
low, int high) {
int i = low;
int j = high;
float x = 0.0;
float y = 0.0;
float z = arr[(low + high) / 2].y;
do {
while(arr[i].y < z) i++;
while(arr[j].y > z) j--;
if(i <= j) {
x = arr[i].x;
y = arr[i].y;
arr[i].x = arr[j].x;
arr[i].y = arr[j].y;
arr[j].x = x;
arr[j].y = y;
i++;
j--;
}
} while(i <= j);
if(low < j)
quicksortY(arr, low, j);
if(i < high)
quicksortY(arr, i, high);
};
/*----------------------------------------*/
void sortX(struct coordinate toado[], int n)
{
quicksortX(toado,0,n-1);
};
/*---------------------------------------------*/
void sortY(struct coordinate toado[], int n)
{
quicksortY(toado,0,n-1);
};
/*-----------------------------------------------
Tao 1 stack luu gia tri tra ve cua ham
closest_pair
Ham push() day 1 gia tri vao stack
Ham pop() lay gia tri ra khi stack
-----------------------------------------------*/
Float stack[100];
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 54 Lớp : K51CHTTT
Int ip=-1;
pthread_mutex_t mutex;
void push(float v) {
pthread_mutex_lock(&mutex); /*
Critical section: Lock */
ip++;
stack[ip] = v;
pthread_mutex_unlock(&mutex);
/*Critical section: Unlock */
return;
}
float pop() {
pthread_mutex_lock(&mutex);
/* Critical section: Lock */
if (ip >= 0) {
float v = stack[ip];
ip--;
pthread_mutex_unlock(&mutex); /*Critical
section: Unlock */
return v;
}
else {
printf("error: ip = %d\n", ip);
pthread_mutex_unlock(&mutex);/* Critical
section: Unlock */
return -11;
}
}
/*-----------------------------------------------
Ham tinh toan khoang cach ngan nhat
-----------------------------------------------*/
void closest_pair(void* args)
{
struct thamso* temp = (struct thamso*) args;
int i,j,mid, countL = 0,countR =
0,tempLeft,tempRight;
float midX,dL,dR,temp1,delta;
struct coordinate tempL[N],tempR[N];
tempLeft = temp->left;
tempRight = temp->right;
mid = (temp->left + temp->right)/2.0;
midX = temp->point[mid].x;
if((temp->right - temp->left) == 1 ) {
push(distance(temp->point[temp-
>right],temp->point[temp->left]));
return;
}
//de qui ben trai,gan lai left =0, right = mid
temp->left = tempLeft;
temp->right = mid;
closest_pair(temp);
dL = pop();
//de qui ben phai,gan lai left = mid, right = N -
1
temp->left = mid;
temp->right = tempRight;
closest_pair(temp);
dR = pop();
// printf("dL = %f dR = %f\n", dL, dR);
delta = min(dL,dR);
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 55 Lớp : K51CHTTT
// Chon nhung phan tu nam trong khoang
delta ben trai
for(i = tempLeft; i < mid; i++)
{
if(abs(temp->point[i].x - midX) <= delta)
{
tempL[countL] = temp->point[i];
countL ++;
}
// Chon nhung phan tu nam trong khoang
delta ben phai
for (i = mid; i < tempRight; i++)
{
if(abs(temp->point[i].x - midX) <= delta)
{
tempR[countR] = temp->point[i];
countR ++;
}
}
// Sap xep nhung phan tu trong khoang delta
theo toa do Y
sortY(tempL,countL);
sortY(tempR,countR);
temp1 = delta;
// Kiem tra xem trong khoang delta co ton tai
cap diem nao
// co khoang cach nho hon delta hay khong
for(i = 0; i < countL; i ++)
{
j = 0;
while((j < countR) && (abs(tempR[j].y -
tempL[i].y) <= 2*delta))
{
j++;
temp1 =
min(temp1,distance(tempL[i],tempR[j])); }
}
delta = min(temp1,delta);
push(delta);
return ;
};
Int run_thread=1;
void* closest_pair_top(void *args)
{
extern int run_thread;
struct thamso *temp = (struct thamso *)args;
int i,j,mid, countL = 0,countR =
0,tempLeft,tempRight,rc;
float dL,dR,temp1,delta,midX;
struct coordinate tempL[N],tempR[N];
pthread_t tid1=-2 , tid2=-2;
tempLeft = temp->left;
tempRight = temp->right;
mid = (N - 1)/2;
midX = temp->point[mid].x;
//de qui ben trai,gan lai left =0, right = mid
temp->left = 0;
temp->right = (N - 1)/2;
if (run_thread) {
rc = pthread_create(&tid1,NULL,(void *)
closest_pair,temp);
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 56 Lớp : K51CHTTT
printf("tid1 = %d\n", tid1);
pthread_join(tid1,NULL);
}
else
closest_pair(temp);
//de qui ben phai,gan lai left = mid, right = N -
1
temp->left = (N - 1)/2;
temp->right = N - 1;
closest_pair(temp);
dR = pop();
dL = pop();
delta = min(dL,dR);
// Chon nhung phan tu nam trong khoang
delta ben trai
for(i = 0; i < (N - 1)/2; i++)
{
if(abs(temp->point[i].x - midX) <= delta)
{
tempL[countL] = temp->point[i];
countL ++;
}
}
// Chon nhung phan tu nam trong khoang
delta ben phai
for (i = (N - 1) /2; i < N - 1; i++)
{
if(abs(temp->point[i].x - midX) <= delta)
{
tempR[countR] = temp->point[i];
countR ++;
}
}
// Sap xep nhung phan tu trong khoang delta
theo toa do Y
sortY(tempL,countL);
sortY(tempR,countR);
temp1 = delta;
// Kiem tra xem trong khoang delta co ton tai
cap diem nao
// co khoang cach nho hon delta hay khong
for(i = 0; i < countL; i ++)
{
j = 0;
while((j < countR) && (abs(tempR[j].y -
tempL[i].y) <= 2*delta))
{
j++;
temp1 =
min(temp1,distance(tempL[i],tempR[j]));
}
}
delta = min(temp1,delta);
push(delta);
return NULL;
};
/*-----------------------------------------------
Ham chinh
-----------------------------------------------*/
int main(){
float result;
double time1,time2;
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 57 Lớp : K51CHTTT
struct thamso temp;
/* Khoi tao bien mutex */
pthread_mutex_init(&mutex, NULL);
/* Khoi tao gia tri left,right ban dau*/
temp.left = 0;
temp.right = N-1;
/*Truyen gia tri cho cac diem*/
input(&temp);
time1 = gettime();
sortX(temp.point,N);
closest_pair_top((void *)&temp);
result = pop();
time2 = gettime();
printf("Thoi gian chay: %lf\n",time2 - time1);
printf("khoang cach ngan nhat la : %2.2f
\n",result);
pthread_mutex_destroy(&mutex);
return 0;
}
Dưới đây là biều đồ so sánh hiệu năng của 3 thuật toán với số các điểm lần lượt là
1000, 4000 và 8000 điểm. Thời gian được tính bằng giây.
N O(n2) O(nlogn) multi‐thread
1000 0.012 0.005 0.003
4000 0.081 0.015 0.007
8000 0.68 0.21 0.012
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 58 Lớp : K51CHTTT
0.012
0.081
0.68
0.005
0.015
0.21
0.003
0.007
0.012
0.001
0.01
0.1
1
1000 4000 8000
O(n2)
O(nlogn)
multi-
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 59 Lớp : K51CHTTT
KẾT LUẬN
Trong khóa luận tốt nghiệp này, tôi đã tìm hiểu về thread, multithread và các lợi thế
của thread và multithread so với tiến trình và đa tiến trình. Cùng với đó, tôi cũng tìm hiểu
về lập trình posix thread trên hệ điều hành linux để nắm được cách tạo thread –
pthread_create(), ngắt thread – pthread_exit(), đồng bộ hóa các thread sử dụng biến
mutex. Ngoài ra, tôi cũng đồng thời cài đặt bài toàn closest_pair theo thuật toán chia để trị
và quan trọng nhất, song song hóa thuật toán này để thấy được hiệu quả của việc sử dụng
multi thread. Biều đồ so sánh trong chương ba đã chỉ rõ rằng, với số điểm cho trước ban
đầu càng lớn, thì hiệu năng tính toán của việc sử dụng multi thread càng lớn so với việc
không sử dụng multi thread.
TÀI LIỆU THAM KHẢO
[1] Clay Breshears , The Art of Concurrency: A Thread Monkey's Guide to Writing
Parallel Applications, Nxb O’Reilly, 2009
[2] Mikhail J. Atallah and Marina Blanton. Algorithms and Theory of Computation
Handbook. Nxb CRC press, 2009.
[3] Multi-Threaded programming with posix thread. Địa chỉ:
thread.html#thread_create_stop.
[4] Multithreading. Địa chỉ:
[5] Posix thread programming. Địa chỉ: https://computing.llnl.gov/tutorials/pthreads/.
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 60 Lớp : K51CHTTT
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