Mutex Programming

การจัดจังหวะการทำงานของเทรด (Thread Synchronization)

Mutex lock (MUTual EXclusion lock) เป็น Semaphore อย่างง่ายที่นำมาใช้ในการจัดการ Mutual Exclusion กับทรัพยากรที่ถูกใช้งานร่วมกันได้อย่างมีประสิทธิภาพโดยเฉพาะกับ thread โดย Mutex จะเป็นตัวแปรที่มีได้ 2 สถานะ คือ สถานะปลดล็อค (unlock) หรือ สถานะล็อค (lock) ซึ่งใช้เพียง 1 บิต (1-bit) ที่แสดงสถานะของมัน แต่ในทางปฏิบัติเราใช้ตัวแปรจำนวนเต็ม (integer) โดยให้สถานะ unlocked เป็นเลขศูนย์ และสถานะ lock จะใช้เลขอื่นแทน

นักพัฒนาสามารถพัฒนาโปรแกรมโดยเรียกใช้ความสามารถในการจัดกลไกการทำงานประสานกันระหว่างเทรด (synchronization mechanisms) โดยไม่ให้เกิดปัญหาในกรณีที่เทรดแต่ละตัวจะต้องเข้าไปใช้ทรัพยากรตัวเดียวกัน ภายในเครื่องคอมพิวเตอร์เดียวกันโดยใช้ไลบรารี pthread ซึ่งจากรูปด้านบนแสดงการอนุญาตเพียง thread #1 ตัวเดียวที่สามารถเข้าไปกำหนดค่า mutex (Mutual Exclusion Lock) เพื่อเข้าถึงทรัพยากรหรือตัวแปร โดยที่ thread #2 จะต้องรอ (block) คิวเพื่อเข้าถึงทรัพยากรหรือตัวแปรหลังจากที่ thread #1 ทำงานเสร็จเรียบร้อยแล้ว ตัว thread #2 จึงจะสามารถเข้าไปกำหนดค่า mutex เพื่อขอเข้าถึงทรัพยากรหรือตัวแปรนั้นต่อไป

แต่อย่างไรก็ตามการใช้ mutex จะถูกใช้ได้เพียงในกรณีมีหลายเทรดในโปรเซสตัวเดียวกันเท่านั้น แต่ในกรณีที่มีหลายโปรเซสต้องการเข้าใช้ทรัพยากรเดียวกัน จะต้องแก้ไขโดยการใช้เทคนิค semaphore แทน mutex ซึ่งจะกล่าวในหัวข้อ 6.2.3 ต่อไป

ฟังก์ชันที่เกี่ยวข้องสำหรับ mutex ภายในไลบรารี pthread ได้แก่

  • pthread_mutex_lock() ใช้สำหรับการเข้าไปล็อคค่าในตัวแปร mutex การเรียกฟังก์ชันนี้ก็จะถูกบล๊อคเอาไว้ไม่ให้เทรดอื่นเข้ามา จนกว่าเทรดตัวที่ใช้งานอยู่ทำการปล่อยตัว mutex เท่านั้น

  • pthread_mutex_unlock() เพื่อปล่อยหรือปลดล็อคค่า mutex จากเทรดที่ใช้ mutex อยู่

  • pthread_mutex_trylock() เพื่อใช้ตรวจสอบตัวแปร mutex ว่ากำลังถูกใช้ล๊อคใช้งานอยู่หรือไม่ ซึ่งจะมีประโยชน์ในการป้องกันไม่ให้เกิดเหตุการณ์ที่เทรดแต่ละตัวต่างฝ่ายต่างรอให้แต่ละตัวเสร็จ (deadlock conditions)

ตัวอย่าง

ตัวอย่างแสดงการสร้างเทรดจำนวน 10 เทรด โดยแต่ละตัวจะเข้าไปใช้งานตัวแปรกลางชื่อว่า counter แต่จะต้องเพิ่มค่า counter ได้เพียงครั้งละ 1 เทรดเท่านั้น

// simple_mutex.c
#include <stdio.h>
#include <pthread.h>
#define NTHREADS 10
void* thread_function(void*);
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int counter = 0;

main() {
	pthread_t thread_id[NTHREADS];
	int i, j;

	for (i = 0; i < NTHREADS; i++) {
		pthread_create(&thread_id[i], NULL, thread_function, NULL);
	}

	for (j = 0; j < NTHREADS; j++) {
		pthread_join(thread_id[j], NULL);
	}
// Now that all threads are complete I can print the final result.     
// Without the join I could be printing a value before all the threads 
// have been completed.                                                

	printf("Final counter value: %d\n", counter);
}

void* thread_function(void *dummyPtr) {
	printf("Thread number %ld\n", pthread_self());
	pthread_mutex_lock(&mutex1);
	counter++;
	pthread_mutex_unlock(&mutex1);
}

คอมไพล์ และทดสอบรันโปรแกรม

$ gcc -o simple_mutex simple_mutex.c –lpthread
$ ./simple_mutex 
Thread number 139638417069824
Thread number 139638425462528
Thread number 139638442247936
Thread number 139638433855232
Thread number 139638408677120
Thread number 139638400284416
Thread number 139638391891712
Thread number 139638383499008
Thread number 139638375106304
Thread number 139638366713600
Final counter value: 10

Last updated

Assoc. Prof. Wiroon Sriborrirux, Founder of Advance Innovation Center (AIC) and Bangsaen Design House (BDH), Electrical Engineering Department, Faculty of Engineering, Burapha University