一. 線程的創建
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp,
const pthread_attr_t *restrict attr,
void *(*start rtn)(void*), void *restrict arg);
返回:成功返回0,否則返回錯誤編號
參數:tidp:線程標識符指針;(存放所創建線程的標識符的地址)
attr:線程屬性指針;
start_rtn:線程運行函數的起始地址;
arg:傳遞給線程運行函數的參數;
注:不能保證新線程和調用線程的執行順序(執行順序由調度算法決定);
下面給出一個具體的案例來說明線程的創建過程。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
//定義線程運行函數
void* th_fn(void *arg)
{
int distance = (int)arg;
int i;
for(i = 1; i <= distance; ++i)
{
printf("%lx run %d\n", pthread_self(), i);
int time = (int)(drand48() * 100000);// 隨機睡眠一定的時間
usleep(time); // 微妙
}
// return (void*)0;
return (void*)distance;
}
int main(void)
{
int err;
pthread_t rabbit, turtle; // 定義線程標識符
// 創建rabbit線程
// 第二個參數是線程的屬性
// 第三個參數是線程運行函數的起始地址
if((err = pthread_create(&rabbit, NULL,
th_fn, (void*)50)) != 0)
{
perror("pthread_create error");
}
//創建turtle線程
if((err == pthread_create(&turtle, NULL,
th_fn, (void*)50)) != 0)
{
perror("pthread_create error");
}
//主控線程調用pthread_join(),自己會阻塞
//直到rabbit線程和turtle線程結束方可運行
// pthread_join(rabbit, NULL);
// pthread_join(turtle, NULL);
//sleep(10); // 主控線程運a行
int result;
pthread_join(rabbit, (void*)&result);
printf("rabbit race distance is: %d\n", result);
pthread_join(turtle, (void*)&result);
printf("turtle race distance is: %d\n", result);
printf("race is finished\n");
printf("control thread id: %lx\n", pthread_self());
printf("finished\n");
return 0;
}
該案例是一個簡易的龜兔賽跑模型。
首先創建兩個線程rabbit和turtle。在pthread_create中,第四個參數時傳遞給線程運行函數得參數。第三個參數是線程運行函數th_fn,這裏我們打印出二者所跑的路程,每跑一步都進行一次睡眠。
說明:a. 在程序中,主控線程調用pthread_join()函數後自己會阻塞,rabbit線程和turtle線程運行結束主控線程方可運行;
b. pthread_join函數中的第二個參數存儲的是線程運行函數的返回結果;
當然,如果在線程運行函數中我們想要輸出更多的內容,也就是參數中包含更多的內容,我們可以將這些內容封裝在一個結構體變量中,然後傳遞給函數,下面的例子對上例做了稍微的改變。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct
{
char name[20]; //存儲線程的名字
int time; //線程睡眠時間
int start;
int end;
}RaceArg;
void* th_fn(void *arg)
{
RaceArg *r = (RaceArg*)arg;
int i = r->start;
for(; i <= r->end; ++i)
{
printf("%s(%lx) running %d\n",
r->name, pthread_self(), i);
usleep(r->time);
}
//return (void*)0;//主動終止
//pthread_exit((void*)0);
return (void*)(r->end - r->start);
}
int main(void)
{
int err;
pthread_t rabbit, turtle;
RaceArg r_a = {"rabbit", (int)(drand48()*100000000), 20, 50};
RaceArg t_a = {"turtle", (int)(drand48()*100000000), 10, 60};
if((err = pthread_create(&rabbit, NULL,
th_fn, (void*)&r_a)) != 0)
{
perror("pthread_create error");
}
if((err = pthread_create(&turtle, NULL,
th_fn, (void*)&t_a)) != 0)
{
perror("pthread_create error");
}
//主控線程調用pthrea_join,自己阻塞
//等待其他線程運行結束自己再運行
// pthread_join(rabbit, NULL);
// pthread_join(turtle, NULL);
int result;
pthread_join(rabbit, (void*)&result);
printf("rabbit race distance is %d\n", result);
pthread_join(turtle, (void*)&result);
printf("turtle race distance is %d\n", result);
printf("race finished\n");
printf("control thread id: %lx\n", pthread_self());
printf("finished.\n");
return 0;
}
在該例子中,我們將線程的名字、每一步的睡眠時間、起始和終止點都封裝在了一個結構體變量中,在線程運行函數中,我們又將該結構體變量作爲參數傳遞給線程運行函數,並將其輸出,最後線程運行函數返回的是總的路程。
注意:在pthread_create中,第四個參數就不再是一個數了,而是一個包含很多內容的結構體變量。同樣,這裏需要主控線程進行阻塞。
問題:那麼在上述的案例中,兩個線程之間會公用一些資源,這期間會不會互相干擾?
我們知道,每個線程都有自己獨立的棧空間,像線程運行函數中的一些局部變量就存儲在這些空間中。當然不同的線程也有一些共享的資源,比如上圖中的數據段部分,該空間存儲着全局變量和靜態變量,由各個線程共享,顯然這樣是不安全的,因爲一個線程對某一個變量做了修改之後,另一個線程訪問該變量時是修改後的。因此,在多線程編程中建議儘量使用局部變量。
二. 線程終止
1. 線程終止方式
- 主動終止:線程的執行函數中調用return語句或者調用pthread_exit()函數;
- 被動終止:線程可以被同一進程的其他線程取消,其他線程調用pthread_cancel(pthid);
#include <pthread.h>
int pthread_cancel(pthread_t pid);
void pthread_exit(void *retval);
int pthread_join(pthread_t th, void **thread_return);
//返回值:成功返回0,否則返回錯誤編號;
函數解釋:#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
typedef struct
{
int d1;
int d2;
}Arg;
void* th_fn(void *arg)
{
Arg *r = (Arg*)arg;
//return (void*)(r->d1 + r->d2);]
return (void*)r;
}
int main(void)
{
int err;
pthread_t th;
Arg r = {20, 50};
if((err = pthread_create(&th, NULL,
th_fn, (void*)&r)) != 0)
{
perror("pthread_create error");
}
/*
int *result;
pthread_join(th, (void**)&result);//第二個參數獲得子線程的返回結果
printf("result is %d\n", (int)result);//這裏做了強制轉換
*/
/*
int result;
pthread_join(th, void(*)&result);
printf("result is %d\n", result);//這裏就不需要強制轉換
*/
int *result;
pthread_join(th, (void**)&result);
printf("result is %d\n",
((Arg*)result)->d1 + ((Arg*)result)->d2);
return 0;
}
該示例就是一個簡單的加法,不再作詳細的解釋了。