Linux - 線程特性舉例

這裏主要演示線程的以下幾個特性
  • 線程共享同一地址空間(全局變量,堆,棧)
  • 線程退出引起進程退出
  • 多線程充分利用多核CPU資源

同組線程共用同一地址空間

  • 全局變量
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//1.共享全局區資源
int g_count = 0;

void* ThreadEntry1(void* arg)
{
    (void)arg;
    while(1)
    {
        ++g_count;
        printf("t1 : %d\n",g_count);
        sleep(1);
    }
    return NULL;
}

void* ThreadEntry2(void* arg)
{
    (void)arg;
    while(1)
    {
        ++g_count;
        printf("t2 : %d\n",g_count);
        sleep(1);
    }
    return NULL;
}

int main()
{
    pthread_t t1,t2;
    pthread_create(&t1,NULL,ThreadEntry1,NULL);
    pthread_create(&t2,NULL,ThreadEntry2,NULL);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    return 0;
}

上面代碼中,有一個全局變量 g_count,兩個線程,同時嘗試去對 全局變量 g_count 進行修改
結果演示:

根據運行結果,我們可以看到,同組線程對全局變量是共享的

  • 堆上的變量
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//2.共享堆上的資源
void* ThreadEntry1(void* arg)
{
    int* p = (int*)arg;
    while(1)
    {
        ++(*p);
        printf("t1 : %d\n",*p);
        sleep(1);
    }
    return NULL;
}

void* ThreadEntry2(void* arg)
{
    int* p = (int*)arg;
    while(1)
    {
        ++(*p);
        printf("t2 : %d\n",*p);
        sleep(1);
    }
    return NULL;
}

int main()
{
    int* p = (int*)malloc(sizeof(int));
    pthread_t t1,t2;
    pthread_create(&t1,NULL,ThreadEntry1,p);
    pthread_create(&t2,NULL,ThreadEntry2,p);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    return 0;
}

上面代碼中,在main函數中malloc出了一段空間,這段空間在堆上,線程1和2同時去修改這個堆上的變量。


  • 棧上的變量
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//3.共享線程棧
//  使用棧上變量比較危險,我們使用前必須保證變量存在
//  但是主線程比較安全,因爲他存在於整個進程
void* ThreadEntry1(void* arg)
{
    int* p = (int*)arg;
    while(1)
    {
        ++(*p);
        printf("t1 : %d\n",*p);
        sleep(1);
    }
    return NULL;
}

void* ThreadEntry2(void* arg)
{
    int* p = (int*)arg;
    while(1)
    {
        ++(*p);
        printf("t2 : %d\n",*p);
        sleep(1);
    }
    return NULL;
}

int main()
{
    int a = 0;
    int* p = &a;
    pthread_t t1,t2;
    pthread_create(&t1,NULL,ThreadEntry1,p);
    pthread_create(&t2,NULL,ThreadEntry2,p);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    return 0;
}

代碼中將主線程棧上一個變量a傳給兩個線程的線程入口函數,線程1和2分別隨主線程棧上的元素進行改變。
注意:
修改棧上的資源是比較危險的,因爲我們說過,每個線程都有自己的一個棧,這個但是這個棧是可以被訪問也可以被改變的,試想,如果線程1已經退出了,線程2嘗試去修改線程1棧上的元素,這是就很可能造成訪問越界!但是,訪問主線程棧上的元素相對比較安全,因爲主線程棧是大家所共享的。




線程退出引起進程退出

線程組中某一個線程訪存異常,都會導致進程異常終止。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//解引用空指針
//會導致硬件設備MMU發現這是一個異常操作
//會給操作系統發送一個異常
//操作系統內核就會給進程發送11號信號,導致進程異常終止 - 段錯誤

//解引用空指針使線程異常終止,觀察
//線程一旦異常終止,就會導致整個進程都結束掉
void* ThreadEntry1(void* arg)
{
    int* p = (int*)arg;
    while(1)
    {
        ++(*p);
        printf("t1 : %d\n",*p);
        int *ptr = NULL;
        *ptr = 10;
        sleep(1);
    }
    return NULL;
}

void* ThreadEntry2(void* arg)
{
    int* p = (int*)arg;
    while(1)
    {
        ++(*p);
        printf("t2 : %d\n",*p);
        sleep(1);
    }
    return NULL;
}

int main()
{
    int a = 0;
    int* p = &a;
    pthread_t t1,t2;
    pthread_create(&t1,NULL,ThreadEntry1,p);
    pthread_create(&t2,NULL,ThreadEntry2,p);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    return 0;
}

線程嘗試對空指針解引用時,進程終止。


多線程充分利用多核CPU資源

  • 場景1,創建對個線程不停歇工作,使用top指令查看CPU利用率
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//利用top指令查看cpu佔用率
void* ThreadEntry(void* arg)
{
    (void)arg;
    while(1)
    {}
    return NULL;
}

int main()
{
    pthread_t tid[32];
    int n = 4;
    int i = 0;
    for(; i < n; ++i)
    {
        pthread_create(&tid[i],NULL,ThreadEntry,NULL);
    }
    for(i = 0; i < n; ++i)
    {
        pthread_join(tid[i],NULL);
    }
    return 0;
}

創建兩個線程,這兩個線程一直在死循環(可換成具體場景)。

如上圖觀察,CPU佔用率可高達400%(四核CPU),證明我們充分利用了CPU資源。

發佈了77 篇原創文章 · 獲贊 50 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章