Linux:執行緒&執行緒建立&執行緒終止
執行緒
1.是作業系統能夠進行排程的最小單位
2.執行緒被包含在程序之中,是程序中的實際運作單位
3.一個執行緒指的是程序中一個單一順序的控制流
4.一個程序可以併發多個執行緒,每個執行緒執行不同的任務
比如四個人在一個房子裡打麻將,建立一個新程序就相當於是在開一個房間,而建立一個新執行緒是在原有的房間中增加一個人而已
執行緒的優點
1.建立一個新的執行緒的代價要比建立一個新程序的代價小得多
2.與程序之間的切換相比,執行緒之間的切換需要作業系統做的工作要少很多
3.執行緒佔有的資源要比程序少
4.執行緒之間共享資料更容易(執行緒是在一個房間中,所以相互通訊比較容易,而程序之間通話需要跑到另一個房間)
執行緒的缺點
1.編碼/除錯難度提高
因為執行緒之間誰先執行不確定,在一個是共享資源的問題
2.缺乏訪問控制
一個執行緒崩潰,會導致整個程序都異常終止,一個執行緒中呼叫某些函式,會影響整個程序
程序與執行緒的區別
1.程序:程序(或者任務)是資源分配的基本單位
2.執行緒:執行緒(或者輕量級程序)是排程/執行的基本單位
3.執行緒執行在程序中
4.一個程序至少都有一個執行緒
程序與執行緒中函式的對比
為什麼執行緒/程序數不是越多越好?
1.執行緒/程序數目達到一定程度時,就不會再提高程式的執行效率了
2.執行緒/程序數目過多可能會導致程序異常終止
3.執行緒/程序數目過多可能會導致執行緒安全問題(執行緒安全:多個執行緒競爭同一資源)
執行緒建立
pthread_create
功能:建立一個新的執行緒
標頭檔案:#include<pthread.h>
原型:
int pthread_create( pthread_t *pthread,
const pthread_attr_t*attr,
void*(*start_routine)(void*),
void *arg )
引數說明:
thread:返回執行緒ID,此引數是一個輸出型引數
(返回的是新建立的執行緒的ID)
attr:設定執行緒的屬性,attr為NULL表示使用預設屬性(一般設為NULL)
(設定的是新執行緒的屬性)
start_routine:是個函式地址,執行緒啟動後需要執行的函式
(這是一個函式指標,指向執行緒的入口函式)
arg:傳給執行緒啟動函式的引數
返回值:成功返回0,失敗返回錯誤碼
執行緒被建立後,並不能保證那個執行緒先執行,新建立的執行緒和呼叫執行緒的執行順序不確定,由作業系統進行排程
示例
1.建立一個執行緒
程式碼:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void*ThreadEntry(void*arg)
{
(void)arg;
printf("I am thread\n");
}
int main()
{
pthread_t tid;
int ret=0;
ret=pthread_create(&tid,NULL,ThreadEntry,NULL);
if(ret!=0)
{
perror("pthread_create");
exit(1);
}
printf("I am main\n");
sleep(1);
//讓主執行緒睡眠1秒,是為了不讓此執行緒跑的太快使整個程序結束
//如果主執行緒跑的太快,新建立的執行緒還沒來的及執行,整個程序就已經結束了
return 0;
}
執行結果:
2.使用死迴圈來建立執行緒(死迴圈是為了防止主執行緒跑的太快,而使新建立的執行緒沒有機會執行,讓它迴圈多次相當於是等待新執行緒啟動和執行)
程式碼:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void*ThreadEntry(void*arg)
{
(void)arg;
while(1)
{
printf("I am thread,%1x\n",pthread_self());
sleep(1);
}
}
int main()
{
int ret=0;
pthread_t tid;
ret=pthread_create(&tid,NULL,ThreadEntry,NULL);
if(ret!=0)
{
perror("pthread_create");
exit(1);
}
while(1)
{
printf("I am main,%1x\n",pthread_self());
sleep(1);
}
return 0;
}
pthread_self()是得到當前執行緒的ID
執行緒建立函式在libpthread.so庫中,所以在編譯時需要將該庫匯入,命令如下:
例如編譯create.c
gcc create.c -lpthread
就是說在編譯時需要加上"-lpthread"
執行結果:
執行緒終止
如果需要終止某個執行緒,而不終止整個程序,有以下三種方法:
1.從需要終止的執行緒函式中
直接return
ps:這種方法對主執行緒不適用,因為從main函式return相當於呼叫exit,會使main函式直接退出
2.需要終止的執行緒可以呼叫pthread_exit()
來終止自己
3.一個執行緒可以呼叫pthread_cancel
來終止同一程序中的其他執行緒
pthread_exit()
1.功能:終止執行緒
2.原型:
void pthread_exit(void *value_ptr)
引數說明:
value_ptr:不要指向一個區域性變數
3.返回值:
無返回值
pthread_cancel()
1.功能:取消一個執行中的執行緒
2.原型:
int pthread_cancel(pthread_t thread)
引數說明:
thread:需要取消的執行緒的ID
3.返回值:
成功返回0,失敗返回錯誤碼
1.直接return
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<pthread.h>
void*ThreadEntry(void*arg)
{
int count=0;
(void)arg;
while(1)
{
++count;
if(count>=5)
return NULL;
printf("I am thread,%1x\n",pthread_self());
sleep(1);
}
}
int main()
{
int ret=0;
pthread_t tid;
ret= pthread_create(&tid,NULL,ThreadEntry,NULL);
if(ret!=0)
{
perror("pthread_create");
exit(1);
}
while(1)
{
printf("I am main,%1x\n",pthread_self());
sleep(1);
}
return 0;
}
執行結果:
2.呼叫pthread_exit()
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
void*ThreadEntry(void *arg)
{
int count=0;
(void)arg;
while(1)
{
++count;
if(count>=7)
pthread_exit(NULL);
printf("I am thread\n");
sleep(1);
}
}
int main()
{
int ret=0;
pthread_t tid;
ret=pthread_create(&tid,NULL,ThreadEntry,NULL);
if(ret!=0)
{
perror("ptherad_create");
exit(1);
}
while(1)
{
printf("I am main\n");
sleep(1);
}
return 0;
}
執行結果:
3.呼叫pthread_cancel()
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
void *ThreadEntry(void*arg)
{
(void)arg;
while(1)
{
printf("I am thread\n");
sleep(1);
}
}
int main()
{
int ret=0;
int count=0;
pthread_t tid;
ret=pthread_create(&tid,NULL,ThreadEntry,NULL);
if(ret!=0)
{
perror("pthread_create");
exit(1);
}
while(1)
{
++count;
if(count>10)
pthread_cancel(tid);
printf("I am main\n");
sleep(1);
}
return 0;
}
執行結果:
pthread_exit
或者return
返回的指標所指向的記憶體單元必須是全域性的或者是用manlloc分配的,不能線上程函式的棧上分配,因為當其它執行緒得到這個返回指標時執行緒函式就已經退出了
示例
1.return返回執行緒函式棧上分配的變數
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void*ThreadEntry(void*arg)
{
(void)arg;
int a=10;
int *p=&a;
printf("I am thread\n");
return (void*)p;
}
int main()
{
pthread_t tid;
void*ret;
int thread=0;
thread=pthread_create(&tid,NULL,ThreadEntry,NULL);
if(thread!=0)
{
perror("pthread_create");
exit(1);
}
printf("I am main\n");
pthread_join(tid,&ret);
printf("ret=%d\n",*(int*)ret);
return 0;
}
2.pthread_exit中的引數是執行緒函式在棧上分配的
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void*ThreadEntry(void*arg)
{
(void)arg;
int count=0;
int a=10;
int *p=&a;
while(1)
{
++count;
if(count>=5)
pthread_exit(p);
printf("I am thread\n");
sleep(1);
}
}
int main()
{
pthread_t tid;
void*ret;
int thread=0;
thread=pthread_create(&tid,NULL,ThreadEntry,NULL);
if(thread!=0)
{
perror("pthread_create");
exit(1);
}
printf("I am main\n");
pthread_join(tid,&ret);
printf("ret=%d\n",*(int*)ret);
return 0;
}
malloc分配
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void*ThreadEntry(void*arg)
{
(void)arg;
int count=0;
int *p=(int*)malloc(sizeof(int));
*p=10;
while(1)
{
++count;
if(count>=5)
pthread_exit(p);
printf("I am thread\n");
sleep(1);
}
}
int main()
{
pthread_t tid;
void*ret;
int thread=0;
thread=pthread_create(&tid,NULL,ThreadEntry,NULL);
if(thread!=0)
{
perror("pthread_create");
exit(1);
}
printf("I am main\n");
pthread_join(tid,&ret);
printf("ret=%d\n",*(int*)ret);
free(ret);//malloc後記得釋放
return 0;
}
全域性變數
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
int a=20;
void*ThreadEntry(void*arg)
{
(void)arg;
int count=0;
int*p=&a;
while(1)
{
++count;
if(count>=5)
pthread_exit(p);
printf("I am thread\n");
sleep(1);
}
}
int main()
{
pthread_t tid;
void*ret;
int thread=0;
thread=pthread_create(&tid,NULL,ThreadEntry,NULL);
if(thread!=0)
{
perror("pthread_create");
exit(1);
}
printf("I am main\n");
pthread_join(tid,&ret);
printf("ret=%d\n",*(int*)ret);
return 0;
}