Linux系統下的多線程遵循POSIX線程接口,稱為 pthread。編寫Linux下的多線程程序,需要使用頭文件pthread.h,連接時需要使用庫libpthread.a。順便說一下,Linux 下pthread的實現是通過系統調用clone()來實現的。clone()是 Linux所特有的系統調用,它的使用方式類似fork,關於clone()的詳細情況,有興趣的讀者可以去查看有關文檔說明。下面我們展示一個最簡單的 多線程程序 pthread_create.c。
一個重要的線程創建函數原型:
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr, void *(*start_rtn)(void),void *restrict arg);
返回值:若是成功建立線程返回0,否則返回錯誤的編號
形式參數:
pthread_t *restrict tidp 要創建的線程的線程id指針
const pthread_attr_t *restrict attr 創建線程時的線程屬性
void* (start_rtn)(void) 返回值是void類型的指針函數
void *restrict arg start_rtn的行參
例程1:
功能:創建一個簡單的線程
程序名稱:pthread_create.c
代碼如下:
[cpp] view
plain copy
#include <stdio.h>
#include <pthread.h>
void *mythread1(void)
{
int i;
for(i = 0; i < 10; i++)
{
printf("This is the 1st pthread,created by xiaoqiang!\n");
sleep(1);
}
}
void *mythread2(void)
{
int i;
for(i = 0; i < 10; i++)
{
printf("This is the 2st pthread,created by xiaoqiang!\n");
sleep(1);
}
}
int main(int argc, const char *argv[])
{
int i = 0;
int ret = 0;
pthread_t id1,id2;
ret = pthread_create(&id1, NULL, (void *)mythread1,NULL);
if(ret)
{
printf("Create pthread error!\n");
return 1;
}
ret = pthread_create(&id2, NULL, (void *)mythread2,NULL);
if(ret)
{
printf("Create pthread error!\n");
return 1;
}
pthread_join(id1,NULL);
pthread_join(id2,NULL);
return 0;
}
執行結果如下:
[cpp] view
plain copy
fs@ubuntu:~/qiang/thread$ vi thread1.c
fs@ubuntu:~/qiang/thread$ gcc -o thread1 thread1.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread1
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
fs@ubuntu:~/qiang/thread$
兩個線程交替執行。
另外,因為pthread的庫不是linux系統的庫,所以在進行編譯的時候要加上-lpthread,否則編譯不過,會出現下面錯誤
thread_test.c: 在函數 ‘create’ 中:
thread_test.c:7: 警告: 在有返回值的函數中,程序流程到達函數尾
/tmp/ccOBJmuD.o: In function `main':thread_test.c:(.text+0x4f):對‘pthread_create’未定義的引用
collect2: ld 返回 1
此例子介紹了創建線程的方法
下面例子介紹向線程傳遞參數。
例程2:
功能:向新的線程傳遞整形值
程序名稱:pthread_int.c
代碼如下:
[cpp] view
plain copy
#include <stdio.h>
#include <pthread.h>
void *create(void *arg)
{
int *num;
num = (int *)arg;
printf("Create parameter is %d\n",*num);
return (void *)0;
}
int main(int argc, const char *argv[])
{
pthread_t id1;
int error;
int test = 4;
int *attr = &test;
error = pthread_create(&id1,NULL,create,(void *)attr);
if(error)
{
printf("Pthread_create is not created!\n");
return -1;
}
sleep(1);
printf("Pthread_create is created..\n");
return 0;
}
執行結果如下:
[cpp] view
plain copy
fs@ubuntu:~/qiang/thread$ vi thread2.c
fs@ubuntu:~/qiang/thread$ gcc -o thread2 thread2.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread2
Create parameter is 4
Pthread_create is created..
fs@ubuntu:~/qiang/thread$
例程總結:
可以看出來,我們在main函數中傳遞的整行指針,傳遞到我們新建的線程函數中。
在上面的例子可以看出來我們向新的線程傳入了另一個線程的int數據,線程之間還可以傳遞字符串或是更復雜的數據結構。
例程3:
程序功能:向新建的線程傳遞字符串
程序名稱:pthread_string.c
代碼如下:
[cpp] view
plain copy
#include <stdio.h>
#include <pthread.h>
void *create(char *arg)
{
char *str;
str = arg;
printf("The parameter passed from main is %s\n",str);
return (void *)0;
}
int main()
{
int error;
pthread_t id1;
char *str1 = "Hello ,xiaoqiang!";
char *attr = str1;
error = pthread_create(&id1, NULL, create, (void *)attr);
if(error != 0)
{
printf("This pthread is not created!\n");
return -1;
}
sleep(1);
printf("pthread is created..\n");
return 0;
}
執行結果如下:
[cpp] view
plain copy
fs@ubuntu:~/qiang/thread$ ./thread3
The parameter passed from main is Hello ,xiaoqiang!
pthread is created..
fs@ubuntu:~/qiang/thread$
例程總結:
可以看出來main函數中的字符串傳入了新建的線程中。
例程4:
程序功能:向新建的線程傳遞字符串
程序名稱:pthread_struct.c
代碼如下:
[cpp] view
plain copy
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
struct menber
{
int a;
char *s;
};
void *create(void *arg)
{
struct menber *temp;
temp = (struct menber *)arg;
printf("menber->a = %d\n",temp->a);
printf("menber->s = %s\n",temp->s);
return (void *)0;
}
int main()
{
int error;
pthread_t id1;
struct menber *p;
p = (struct menber *)malloc(sizeof(struct menber));
p->a = 1;
p->s = "xiaoqiang!";
error = pthread_create(&id1,NULL,create,(void *)p);
if(error)
{
printf("pthread is not created!\n");
return -1;
}
sleep(1);
printf("pthread is created!\n");
free(p);
p = NULL;
return 0;
}
執行結果如下:
[cpp] view
plain copy
fs@ubuntu:~/qiang/thread$ vi thread4.c
fs@ubuntu:~/qiang/thread$ gcc -o thread4 thread4.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread4
menber->a = 1
menber->s = xiaoqiang!
pthread is created!
fs@ubuntu:~/qiang/thread$
例程總結:
可以看出來main函數中的一個結構體傳入了新建的線程中。
線程包含了標識進程內執行環境必須的信息。他集成了進程中的所有信息都是對線程進行共享的,包括文本程序、程序的全局內存和堆內存、棧以及文件描述符
例程5:
程序目的:驗證新建立的線程可以共享進程中的數據
程序名稱:pthread_share.c
代碼如下:
[cpp] view
plain copy
#include <stdio.h>
#include <pthread.h>
static int a = 5;
void *create(void *arg)
{
printf("New pthread...\n");
printf("a = %d\n",a);
return (void *)0;
}
int main(int argc, const char *argv[])
{
int error;
pthread_t id1;
error = pthread_create(&id1, NULL, create, NULL);
if(error != 0)
{
printf("new thread is not created!\n");
return -1;
}
sleep(1);
printf("New thread is created...\n");
return 0;
}
結果如下:
[cpp] view
plain copy
fs@ubuntu:~/qiang/thread$ vi thread5.c
fs@ubuntu:~/qiang/thread$ gcc -o thread5 thread5.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread5
New pthread...
a = 5
New thread is created...
fs@ubuntu:~/qiang/thread$
例程總結:
可以看出來,我們在主線程更改了我們的全局變量a的值的時候,我們新建立的線程則打印出來了改變的值,可以看出可以訪問線程所在進程中的數據信息。
2、線程的終止
如果進程中任何一個線程中調用exit,_Exit,或者是_exit,那麼整個進程就會終止,
與此類似,如果信號的默認的動作是終止進程,那麼,把該信號發送到線程會終止進程。
線程的正常退出的方式:
(1) 線程只是從啟動例程中返回,返回值是線程中的退出碼
(2) 線程可以被另一個進程進行終止
(3) 線程自己調用pthread_exit函數
兩個重要的函數原型:
[cpp] view
plain copy
include <pthread.h>
void pthread_exit(void *rval_ptr);
/*rval_ptr 線程退出返回的指針*/
int pthread_join(pthread_t thread,void **rval_ptr);
/*成功結束進程為0,否則為錯誤編碼*/
pthread_join使一個線程等待另一個線程結束。
代碼中如果沒有pthread_join主線程會很快結束從而使整個進程結束,從而使創建的線程沒有機會開始執行就結束了。加入pthread_join後,主線程會一直等待直到等待的線程結束自己才結束,使創建的線程有機會執行。
頭文件 : #include <pthread.h>
函數定義: int pthread_join(pthread_t thread, void **retval);
描述 :pthread_join()函數,以阻塞的方式等待thread指定的線程結束。當函數返回時,被等待線程的資源被收回。如果線程已經結束,那麼該函數會立即返回。並且thread指定的線程必須是joinable的。
參數 :thread: 線程標識符,即線程ID,標識唯一線程。retval:
用戶定義的指針,用來存儲被等待線程的返回值。
返回值 : 0代表成功。 失敗,返回的則是錯誤號。
例程6
程序目的:線程正常退出,接受線程退出的返回碼
程序名稱:pthread_exit.c
執行代碼如下:
[cpp] view
plain copy
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *create(void *arg)
{
printf("new thread is created ... \n");
return (void *)0;
}
int main(int argc,char *argv[])
{
pthread_t tid;
int error;
void *temp;
error = pthread_create(&tid, NULL, create, NULL);
if( error )
{
printf("thread is not created ... \n");
return -1;
}
error = pthread_join(tid, &temp);
if( error )
{
printf("thread is not exit ... \n");
return -2;
}
printf("thread is exit code %d \n", (int )temp);
return 0;
}
執行結果如下:
[cpp] view
plain copy
fs@ubuntu:~/qiang/thread$ vi thread6.c
fs@ubuntu:~/qiang/thread$ gcc -o thread6 thread6.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread6
new thread is created ...
thread is exit code 0
fs@ubuntu:~/qiang/thread$
例程總結:
可以看出來,線程退出可以返回線程的int數值。
線程退出不僅僅可以返回線程的int數值,還可以返回一個復雜的數據結構
例程7
程序目的:線程結束返回一個復雜的數據結構代碼如下:
[cpp] view
plain copy
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
struct menber
{
int a;
char *b;
}temp={8,"xiaoqiang"};
void *create(void *arg)
{
printf("new thread ... \n");
return (void *)&temp;
}
int main(int argc,char *argv[])
{
int error;
pthread_t tid;
struct menber *c;
error = pthread_create(&tid, NULL, create, NULL);
if( error )
{
printf("new thread is not created ... \n");
return -1;
}
printf("main ... \n");
error = pthread_join(tid,(void *)&c);
if( error )
{
printf("new thread is not exit ... \n");
return -2;
}
printf("c->a = %d \n",c->a);
printf("c->b = %s \n",c->b);
sleep(1);
return 0;
}
執行結果如下:
[cpp] view
plain copy
fs@ubuntu:~/qiang/thread$ gcc -o thread7 thread7.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread7
main ...
new thread ...
c->a = 8
c->b = xiaoqiang
fs@ubuntu:~/qiang/thread$
例程總結:
一定要記得返回的數據結構要是在這個數據要返回的結構沒有釋放的時候應用,如果數據結構已經發生變化,那返回的就不會是我們所需要的,而是髒數據。
3、線程標識
函數原型:
#include <pthread.h>
pthread_t pthread_self(void);
pid_t getpid(void);
getpid()用來取得目前進程的進程識別碼,函數說明
例程8
程序目的:實現在新建立的線程中打印該線程的id和進程id代碼如下:
[cpp] view
plain copy
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> /*getpid()*/
void *create(void *arg)
{
printf("New thread .... \n");
printf("This thread's id is %u \n", (unsigned int)pthread_self());
printf("The process pid is %d \n",getpid());
return (void *)0;
}
int main(int argc,char *argv[])
{
pthread_t tid;
int error;
printf("Main thread is starting ... \n");
error = pthread_create(&tid, NULL, create, NULL);
if(error)
{
printf("thread is not created ... \n");
return -1;
}
printf("The main process's pid is %d \n",getpid());
sleep(1);
return 0;
}
<span ></span>
執行結果如下:
[cpp] view
plain copy
fs@ubuntu:~/qiang/thread$ gcc -o thread8 thread8.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread8
Main thread is starting ...
The main process's pid is 4955
New thread ....
This thread's id is 3075853120
The process pid is 4955
fs@ubuntu:~/qiang/thread$