1. 程式人生 > >Linux:執行緒&執行緒建立&執行緒終止

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;
}

這裡寫圖片描述