Lamport's bakery algorithm experiment in C
- ランポートのパン屋のアルゴリズム例をC言語で書いてみる
- PCDPはもう大体良いといわれたけど、微妙にわかってないので実装してみる。
- まずはじめに、沢山のthreadで排他処理せずに、共有メモリをインクリメントするようなコードを書く。
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <pthread.h>
#define THREAD_LEN 10
long int acc;
void* thread_temp( void* arg )
{
long int i;
for (i=0; i < 100000000; i++){
acc = (acc + 1);
}
}
int main(int args, char *argv)
{
int thread_len = THREAD_LEN;
int i;
pthread_t threads[thread_len];
for(i=0; i<thread_len; i++){
printf("[%d] create thread.\n", i);
pthread_create( &(threads[i]), NULL, thread_temp , NULL);
}
for(i=0; i<thread_len; i++){
printf("[%d] start thread.\n", i);
pthread_join( threads[i] , NULL );
}
printf("*** result acc = %d\n", acc);
return 0;
}
[0] create thread.
[1] create thread.
[2] create thread.
[3] create thread.
[4] create thread.
[5] create thread.
[6] create thread.
[7] create thread.
[8] create thread.
[9] create thread.
[0] start thread.
[1] start thread.
[2] start thread.
[3] start thread.
[4] start thread.
[5] start thread.
[6] start thread.
[7] start thread.
[8] start thread.
[9] start thread.
*** result acc = 423256012( <- 1000000000にならない !!!!!!!!)
- これは排他処理を行ってないので、アトミックでなければならない処理がインターリーブ上で分割されてしまったためだ。
- そこで、「ランポートのパン屋のアルゴリズム」によって排他処理してみる。
// wikipediaによるアルゴリズム
// 広域変数の宣言と初期値
Enter, Number: array [1..N] of integer = {0};
1 Thread(i) {
2 while (true) {
3 Enter[i] = 1;
4 Number[i] = 1 + max(Number[1], ..., Number[N]);
5 Enter[i] = 0;
6 for (j = 1; j <= N; j++) {
7 while (Enter[j] != 0) {
8 // スレッド j が番号を得るまで待つ
9 }
10 while ((Number[j] != 0) && ((Number[j], j) < (Number[i], i))) {
11 // 小さい番号を持つスレッドや自分と同じだが高い優先順位の
12 // スレッドが処理を終えるのを待つ
13 }
14 }
15 // クリティカルセクション...
16 Number[i] = 0;
17 // 非クリティカルセクション...
18 }
19 }
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define THREAD_LEN 10
int g_enter[THREAD_LEN] = {0};
int g_number[THREAD_LEN] = {0};
long int acc;
int check_mvlt(int a, int b, int c, int d)
{
return ((a < c) || ((a == c) && (b < d)));
}
int max( int arr[], size_t size){
int i;
int max = arr[0];
for(i=1; i<size; i++)
if(max < arr[i])
max = arr[i];
return max;
}
void* thread_temp( void* args )
{
int id = *((int*)args);
long int i;
int j;
int thread_len = THREAD_LEN;
printf("+++ myid is %d\n", id);
g_enter[id] = 1;
g_number[id] = 1 + max(g_number, thread_len);
g_enter[id] = 0;
for (j=0; j<thread_len; j++) {
while (g_enter[j] != 0) {
}
while ((g_number[j] != 0) &&
check_mvlt(g_number[j], j, g_number[id], id) )
{
}
}
for (i=0; i < 100000000; i++)
acc = (acc + 1);
g_number[id] = 0;
}
int main(int args, char *argv)
{
int thread_len = THREAD_LEN;
int j[thread_len];
int i;
pthread_t threads[thread_len];
for(i=0; i < thread_len; i++) j[i] = i;
for(i=0; i<thread_len; i++){
printf("[%d] create thread.\n", i);
pthread_create( &(threads[i]), NULL, thread_temp, (void*)(j+i) );
}
for(i=0; i<thread_len; i++){
printf("[%d] start thread.\n", i);
pthread_join( threads[i] , NULL );
}
printf("*** result acc = %d\n", acc);
return 0;
}
[0] create thread.
[1] create thread.
[2] create thread.
[3] create thread.
[4] create thread.
[5] create thread.
[6] create thread.
[7] create thread.
[8] create thread.
[9] create thread.
[0] start thread.
+++ myid is 0
+++ myid is 1
+++ myid is 2
+++ myid is 3
+++ myid is 4
+++ myid is 5
+++ myid is 6
+++ myid is 7
+++ myid is 8
+++ myid is 9
[1] start thread.
[2] start thread.
[3] start thread.
[4] start thread.
[5] start thread.
[6] start thread.
[7] start thread.
[8] start thread.
[9] start thread.
*** result acc = 1000000000
- うまくいっている。
- ちなみにlock,unlockは関数で囲えば、もっときれいになるだろう。
void m_lock(int id)
{
int j;
int thread_len = THREAD_LEN;
g_enter[id] = 1;
g_number[id] = 1 + max(g_number, thread_len);
g_enter[id] = 0;
for (j=0; j<thread_len; j++) {
while (g_enter[j] != 0) {
}
while ((g_number[j] != 0) &&
check_mvlt(g_number[j], j, g_number[id], id) )
{
}
}
}
void m_unlock(int id)
{
g_number[id] = 0;
}
void* thread_temp( void* args )
{
int id = *((int*)args);
long int i;
int thread_len = THREAD_LEN;
printf("+++ myid is %d\n", id);
m_lock(id);
for (i=0; i < 100000000; i++)
acc = (acc + 1);
m_unlock(id);
}