Sử dụng thư viện pthead

      Comments Off on Sử dụng thư viện pthead

(Sưu tầm)

Thư viện POSIX thread là một chuẩn cho lập trình thread trong C/C++. Nó cho phép bạn tạo ra các ứng dụng chạy song song theo luồng, rất hiệu quả trên hệ thống nhiều bộ vi xử lý hoặc bộ vi xử lý nhiều nhân ở đó các luồng xử lý có thể được lập lịch chạy trên các bộ xử lý khác nhau do đó tăng được tốc độ xử lý song song hoặc xử lý phân tán.

Tham khảo bài viết hướng dẫn cài đặt pthread trên Visual Studio.

Cơ bản về thread

  • Các thao tác của thread bao gồm tạo, kết thúc, đồng bộ (hơp – join, khoá – blocking), lập lịch, quản lý dữ liệu và tương tác tiến trình.
  • Một thread không duy trì một danh sách các thread được tạo ra và  cũng nó như không biết thread nào tạo ra nó.
  • Các thread trong cùng một tiến trình chia sẻ cùng không gian địa chỉ.
  • Các thread cùng tiến trình chia sẻ:
    • Chỉ thị lệnh của tiếng trình
    • Hầu hết dữ liệu
    • Mở các tập tin (các mô tả)
    • Các tín hiệu và xử lý tính hiệu
    • Thư mục làm việc hiện thời
    • ID người sử dụng và nhóm sử dụng
  • Mỗi thread có duy nhất:
    • Thread ID
    • Tập các thanh ghi, con trỏ stack
    • Stack cho các biến cục bộ, trả về địa chỉ
    • Mặt nạ tính hiệu
    • Sự ưu tiên
    • Giá trị trả về: errno
  • Hàm pthread trả về “0” nếu nó được tạo thành công.

Ví dụ 1:

#include <conio.h>
#include "pthread.h"
#include <stdio.h>

void * printx(void * data)
{
	while(1)
	{
		printf("x");
	}
}

int main(int argc, char * argv[])
{
	pthread_t idthread;
	pthread_create(&idthread,NULL,&printx,NULL);
	while(1)
	{
		printf("o");
	}
	getch();
	return 0;
}

Giải thích

       int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);

Chức năng của hàm pthread_create tạo mới một thread. Tham số đầu vào là một biến của thread thuộc kiểu pthread_t, một thuộc tính của thead thuộc kiểu pthread_attr_t, đặt là NULL nếu giữ thuộc tính thread mặc định, một thủ tục kiểu void và một con trỏ đối số cho hàm thuộc kiểu void.
Ví dụ 1 thực hiện in ra xen kẽ vô hạn nhau ký tự “o” và ký tự “x”. Trong chương trình hàm main sẽ là một luồng chính và một luồng gọi hàm printx.

Đồng bộ thread

Thư viện thread cung cấp ba cơ chế đồng bộ:

  • mutexes – Khoá loại trừ lẫn nhau: Khoá việc truy cập các biến của các thead khác. Do đó thực thi này có toàn quyeenf truy cập và thiết lập cho các biến.
  • joins – Thiết lập một thread đợi cho đến khi những thread khác hoàn thành.
  • Các biến điều kiện – kiểu dữ liệu pthread_cond_t

Ví dụ 2

#include "pthread.h"
#include <stdio.h>
#include <conio.h>

int couter = 0;
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
void * funcC(void *data)
{
	pthread_mutex_lock(&mutex1);
	couter += 100;
	printf("Gia tri couter trong Func:%d\n",couter);
	pthread_mutex_unlock(&mutex1);
	return NULL;
}
void * funcC1(void *data)
{
	pthread_mutex_lock(&mutex1);
	couter += 200;
	printf("Gia tri couter trong Func1:%d\n",couter);
	pthread_mutex_unlock(&mutex1);
	return NULL;
}
int main(int argc, char * argv[])
{
	pthread_t thread1, thread2;
	pthread_create(&thread1,NULL,&funcC,NULL);
	pthread_create(&thread2,NULL,&funcC1,NULL);
	pthread_join(thread1,NULL);
	pthread_join(thread2,NULL);
	printf("Gia tri cuoi cung cua counter: %d\n",couter);
	getch();
	return 0;
}

Giải thích

      int pthread_join(pthread_t th, void **thread_return);
  •  pthread_join() được sử dụng để đợi cho việc kết thúc của một thread hoặc chờ để tái kết hợp với luồng chính của hàm main.
  • pthread_mutex_lock() – giữ  khóa trên biến mutex chỉ định. Nếu mutex đã bị khoá bở một thread khác, lời gọi này sẽ bị giữ lại cho tới khi mutex này mở khoá.
  • pthread_mutex_unlock() – mở khoá biến mutex. Một lỗi trả về nếu mutex đã mở khoá hoặc được sở hữu của một thread khác.
  • pthread_mutex_trylock() – thử khoá một mutex hoặc sẽ trả về mã lỗi nếu bận. Hữu ích cho việc ngăn ngừa trường hợp khoá chết (deadlock).

Ví dụ 3

#include <stdio.h>
#include "pthread.h"

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;
void * funcC1(void * data);
void * funcC2(void * data);
int count = 0;
#define COUNT_DONE 10
#define COUNT_HALT1 3
#define COUNT_HALT2 6
int main(int argc, char * argv[])
{
	pthread_t tid1, tid2;
	pthread_create(&tid1,NULL,&funcC1,NULL);
	pthread_create(&tid2,NULL,&funcC2,NULL);

	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);
    
	printf("Gia tri counter cuoi cung: %d\n",count);

	getch();
	return 0;
}
//Viết các số từ 1-3 và 8-10 bởi thủ tục funcC1()
void * funcC1(void * data)
{
	for(;;)
	{
		//Khóa mutex và đợi tín hiệu để giải phóng mutex
		pthread_mutex_lock( &mutex1);
		//Đợi cho thủ tục funcC2() thao tác trên biến count
		//mutex1 mở khóa nếu biến cond1 nhận được tín hiệu của funcC2()
		pthread_cond_wait( &cond1, &mutex1);
		count++;
		printf("Gia tri counter cua funcC1: %d\n",count);
		pthread_mutex_unlock( &mutex1);
		if(count >= COUNT_DONE) return(NULL);
	}
}
//Viết các số từ 4-7 bởi thủ tục funcC2()
void * funcC2(void * data)
{
	for(;;)
	{
		pthread_mutex_lock( &mutex1);
		if( count < COUNT_HALT1 || count > COUNT_HALT2 )
        {
			//nếu giá trị count khớp với điều kiện
			//tín hiệu giải phóng chờ cho biến mutex
			//funcC1()được phép chỉnh sửa giá trị của count
			pthread_cond_signal( &cond1);
		}
		else
		{
			count++;
			printf("Gia tri counter cua funcC2: %d\n",count);
		}
		pthread_mutex_unlock( &mutex1);
		if(count >= COUNT_DONE) return(NULL);
	}
}

Giải thích

Biến điều kiện là một biến kiểu pthread_cond_t và được sử dụng với các hàm thích hợp để một thread đợi và sau đó lại tiếp tục xử lý. Kỹ thuật này cho phép các thread dừng thực thi và huỷ bỏ xử lý cho đến khi một vài điều kiện là true. Biến điều kiện luôn luôn phải kết hợp với một mutex. Thread sẽ chờ mãi mãi nếu tín hiệu không được gửi. Bất kỳ mutex nào cũng có thể được sử dụng, không có liên kếtt tường minh giữa mutex và biến điều kiện.

Các hàm được sử dụng trong biến điều kiện:

  • Khởi tạo/Huỷ:
    • pthread_cond_init()
    • pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    • pthread_cond_destroy()
  • Đợi dựa trên điều kiện:
    • pthread_cond_wait() – khoá mutex và đợi tính hiệu của biến điều kiện
    • pthread_cond_timedwait() – đặt giới hạn về thời gian bao lâu thì sẽ khoá
  • Đánh thức thread dựa trên điều kiện:
    • pthread_cond_signal() – khởi động một thread đang đợi bởi biến điều kiện.
    • pthread_cond_broadcast() – đánh tức tất cả các thread được khoá bởi biến điều kiện

Giải thích thêm về pthread_mutex_trylock(). Nó là hàm non-blocking chỉ kiểm tra tình trạng của mutex mà thôi ví dụ như sau:

int error = pthread_mutex_trylock(&lock);
if (error == 0) {
    /*... have the lock */
    pthread_mutex_unlock(&lock);
} else if (error == EBUSY) {
    /*... failed to get the lock because another thread holds lock */
} else if (error == EOWNERDEAD) {
    /*... got the lock, but the critical section state may not be consistent */
    if (make_state_consistent_succeeds()) {
        pthread_mutex_consistent(&lock);
        /*... things are good now */
        pthread_mutex_unlock(&lock);
    } else {
        /*... abort()? */
    }
} else {
    switch (error) {
    case EAGAIN: /*... recursively locked too many times */
    case EINVAL: /*... thread priority higher than mutex priority ceiling */
    case ENOTRECOVERABLE:
                 /*... mutex suffered EOWNERDEAD, and is no longer consistent */
    default:
        /*...some other as yet undocumented failure reason */
    }
}