引言:題目具體描述記不大清了,大概是:Linux平臺,利用線程調度的隨機性和sleep的不準確性,生成一個各位均不相同的字符數組的僞隨機序列。不得使用任何庫函數。(這句記得清楚,當時在想線程庫算不算,題目的意思應該是:不得使用庫提供的隨機函數)
1.算法
當時讀完題很開心,這題可以用與“《編程珠璣》取樣問題(ch12, p119)”類似的算法解決。算法如下——除了第一字符(下標0)以外,爲其餘N-1個字符各創建一個線程,每個線程先sleep一秒(也可以更長),再將對應位置的字符和第一個字符交換;N-1個線程完成後,主線程結束。原理暗藏在題目中,sleep一秒後,因爲sleep的不準確性,這N-1個線程幾乎同時醒來(就緒)(試想如若sleep非常精確,各個線程醒來的順序就會和創建順序相同);又由於線程調度的隨機性,這時會被執行的線程是隨機的,(不知先後順序地)執行N-1次之前所述的交換所得的便是一個僞隨機序列。不過當時想不起來pthread_create幾個參數的順序了(前面題的計算量不小,頭都搞暈了),就隨便按個順序寫了。回來後,按照當時的思路很快在電腦上寫了出來:
#include
#include
#include
#include
#ifdef WIN32
# include
#define sleep(x) Sleep(1000 * x)
#endif
char str[] = "ABCDEFGH";
#define LEN sizeof(str) / sizeof(str[0]) - 1
pthread_t tids[LEN];
void* thread_func(void *idx)
{
char tmp;
sleep(1);
tmp = str[(int)idx];
str[(int)idx] = str[0]; // str[0] is the critical resource !!
str[0] = tmp;
return (void*)0;
}
int main(void)
{
int i;
puts(str);
for(i=1; i
2.互斥
不過一在電腦上寫出來立即意識到一個問題——第一個元素是臨界資源(所有“其他線程”都想搶着用這塊地);如果不做互斥訪問可能會出現——有的字符出現兩次(獲更多)有的字符沒了,這種錯誤不是每次都會出現:意識到這個錯誤之後,比較容易修改,只需將線程函數內對第一個元素的操作放入臨界區中即可:
#include
#include
#include
#include
#ifdef WIN32
# include
#define sleep(x) Sleep(1000 * x)
#endif
char str[] = "ABCDEFGH";
#define LEN sizeof(str) / sizeof(str[0]) - 1
pthread_t tids[LEN];
pthread_mutex_t mutex;
void* thread_func(void *idx)
{
char tmp;
sleep(1);
tmp = str[(int)idx];
pthread_mutex_lock(&mutex);
str[(int)idx] = str[0]; // str[0] is the critical resource !!
str[0] = tmp;
pthread_mutex_unlock(&mutex);
return (void*)0;
}
int main(void)
{
int i;
puts(str);
pthread_mutex_init(&mutex, NULL);
for(i=1; i
這次不會再有錯誤。for命令連續測試10次:
到此,說明這個算法沒有問題。
3.同步
還應該main裏的sleeep(2)改掉。main創建好N-1個線程後就應該被掛起,直到其他所有線程都“完工”後才應該被喚醒;肯定要用條件變量,main創建好其他線程後wait阻塞,其他所有線程都“完工”再被signal喚醒。爲了保證這樣的順序,必須要讓最後一個完工的線程知道自己是最後一個,也就是在最後一個其他線程“收工”的時候signal。只需要加個計數變量count用來標記還有多少線程沒有完工,將其初始化爲要創建的線程數N-1,每有一個線程“完工”就count--,判斷count的值即可知道當前線程是倒數第幾個完工的。(囉嗦一堆,還是上代碼直觀):
#include
#include
#include
#include
#ifdef WIN32
# include
#define sleep(x) Sleep(1000 * x)
#endif
char str[] = "ABCDEFGH";
#define LEN sizeof(str) / sizeof(str[0]) - 1
int count;
pthread_t tids[LEN];
pthread_mutex_t mutex;
pthread_cond_t cond;
void* thread_func(void *idx)
{
char tmp; // in TLS(Thread local space.)
sleep(1);
tmp = str[(long)idx];
pthread_mutex_lock(&mutex);
str[(long)idx] = str[0]; // str[0] is the critical resource.
str[0] = tmp;
--count;
printf("%ld %d\n", (long)idx, count);
if(count == 0) {
puts("SIGNAL");
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex);
return (void*)0;
}
int main(void)
{
long i;
puts(str);
count = LEN-1;
pthread_mutex_init(&mutex, 0);
pthread_cond_init(&cond, 0);
for(i=1; i 0) {
pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);
puts(str);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return EXIT_SUCCESS;
}