pthread線程知識要點

pthread線程知識要點多線程

線程之間通訊的兩個基本問題是互斥和同步。`併發

#include<pthread.h>ide

1、pthread_create
一、函數原型
int pthread_create(pthread_t tidp,const pthread_attr_t attr,(void)(start_rtn)(void),void arg);函數

二、函數功能:
pthread_create是類Unix操做系統(Unix、Linux、Mac OS X等)的建立線程的函數。
它的功能是建立線程(實際上就是肯定調用該線程函數的入口點),在線程建立之後,就開始運行相關的線程函數。post

返回成功時,由tidp指向的內存單元被設置爲新建立線程的線程ID。attr參數用於指定各類不一樣的線程屬性。
新建立的線程從start_rtn函數的地址開始運行,該函數只有一個萬能指針參數arg,
若是須要向start_rtn函數傳遞的參數不止一個,那麼須要把這些參數放到一個結構中,而後把這個結構的地址做爲arg的參數傳入。

三、返回值:
表示成功,返回0;表示出錯,返回-1。操作系統

四、參數
第一個參數爲指向線程標識符的指針。
第二個參數用來設置線程屬性。
第三個參數是線程運行函數的起始地址。
最後一個參數是運行函數的參數。線程

示例:
// 線程的運行函數
void say_hello(void args)
{
cout << "Hello Runoob!" << endl;
return 0;
}指針

int main()
{
    int indexes = 10;
    pthread_t tids;
    int ret = pthread_create(&tids, NULL, say_hello, (void *)&(indexe));
    pthread_exit(NULL);
}

2、pthread_join
一、函數定義:
int pthread_join(pthread_t thread, void **retval);
二、函數功能:
pthread_join() 子程序阻礙調用程序,直到指定的 threadid 線程終止爲止code

3、線程存在的問題和臨界區
前面咱們知道了怎麼建立線程,下面咱們再來看看這樣一個實例,建立100個線程,它們都訪問了同一變量,
其中一半對這個變量進行加1操做,一半進行減1操做,按道理其結果會等於0.內存

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREAD 100
void * thread_inc(void * arg);
void * thread_des(void * arg);
long long num = 0;   //long long類型是64位整數型,多線程共同訪問

int main(int argc, char *argv[])
{
    pthread_t thread_id[NUM_THREAD];
    int i;

    //建立100個線程,一半執行thread_inc,一半執行thread_des
    for(i = 0; i < NUM_THREAD; i++)
    {
        if(i %2)
            pthread_create(&(thread_id[i]), NULL, thread_inc, NULL);
        else
            pthread_create(&(thread_id[i]), NULL, thread_des, NULL);
    }

    //等待線程返回
    for (i = 0; i < NUM_THREAD; i++)
        pthread_join(thread_id[i], NULL);

    printf("result: %lld \n", num);  //+1,-1按道理結果是0
    return 0;
}

//線程入口函數1
void * thread_inc(void * arg)
{
    for (int i = 0; i < 50000000; i++)
        num += 1;//臨界區(引發問題的語句就是臨界區位置)
    return NULL;
}

//線程入口函數2
void * thread_des(void * arg)
{
    for (int i = 0; i < 50000000; i++)
        num -= 1;//臨界區
    return NULL;
}

從運行結果看並非0,並且每次運行的結果都不一樣。那這是什麼緣由引發的呢? 
是由於每一個線程訪問一個變量是這樣一個過程:先從內存取出這個變量值到CPU,
而後CPU計算獲得改變後的值,最後再將這個改變後的值寫回內存。所以,咱們能夠很容易看出,
多個線程訪問同一變量,若是某個線程還只剛從內存取出數據,還沒來得及寫回內存,這時其它線程又訪問了這個變量,因此這個值就會不正確了。

3.一、接下來咱們再來說講怎麼解決這個問題:線程同步
    互斥量和信號量。

    互斥:
    互斥量技術從字面也能夠理解,就是臨界區有線程訪問,其它線程就得排隊等待,它們的訪問是互斥的,實現方式就是給臨界區加鎖與釋放鎖。

    #include <pthread.h>

    int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);  //建立互斥量

    int pthread_mutex_destroy(pthread_mutex_t *mutex);//銷燬互斥量

    int pthread_mutex_lock(pthread_mutex_t *mutex);//加鎖

    int pthread_mutex_unlock(pthread_mutex_t *mutex);//釋放鎖
    臨界區圍住必定要lock和unlock一一對應。
    /*擴展臨界區,減小加鎖,釋放鎖調用次數,但這樣變量必須加滿到50000000次後其它線程才能訪問.
    這樣是延長了線程的等待時間,但縮短了加鎖,釋放鎖函數調用的時間,這裏沒有定論,本身酌情考慮*/
    void * thread_inc(void * arg)
    {
        pthread_mutex_lock(&mutex);  //互斥量鎖住
        for (int i = 0; i < 1000000; i++)
            num += 1;
        pthread_mutex_unlock(&mutex); //互斥量釋放鎖
        return NULL;
    }

    /*縮短了線程等待時間,但循環建立,釋放鎖函數調用時間增長*/
    void * thread_des(void * arg)
    {
        for (int i = 0; i < 1000000; i++)
        {
            pthread_mutex_lock(&mutex);
            num -= 1;
            pthread_mutex_unlock(&mutex);
        }
        return NULL;
    }

    信號量:
    號量與互斥量相似,只是互斥量是用鎖來控制線程訪問而信號量是用二進制0,1來完成控制線程順序。
    sem_post信號量加1,sem_wait信號量減1,當信號量爲0時,sem_wait就會阻斷,所以經過這樣讓信號量加1減1就能控制線程的執行順序了。

    #include <semaphore.h>

    int sem_init(sem_t *sem, int pshared, unsigned int value);//建立信號量
    int sem_destroy(sem_t *sem);//銷燬信號量
    int sem_post(sem_t *sem);//信號量加1
    int sem_wait(sem_t *sem);//信號量減1,爲0時阻塞

    #include <stdio.h>
    #include <pthread.h>
    #include <semaphore.h>

    void * read(void * arg);
    void * accu(void * arg);
    static sem_t sem_one;
    static sem_t sem_two;
    static int num;

    int main(int argc, char *argv[])
    {
        pthread_t id_t1, id_t2;
        sem_init(&sem_one, 0, 0);
        sem_init(&sem_two, 0, 1);

        pthread_create(&id_t1, NULL, read, NULL);
        pthread_create(&id_t2, NULL, accu, NULL);

        pthread_join(id_t1, NULL);
        pthread_join(id_t2, NULL);

        sem_destroy(&sem_one);
        sem_destroy(&sem_two);

        return 0;
    }

    void * read(void * arg)
    {
        int i;
        for (i = 0; i < 5; i++) {
            fputs("Input num: ", stdout);
            sem_wait(&sem_two);
            scanf("%d", &num);
            sem_post(&sem_one);
        }
        return NULL;
    }

    void * accu(void * arg)
    {
        int sum = 0 , i;
        for (i = 0; i < 5; i++) {
            sem_wait(&sem_one);
            sum+= num;
            sem_post(&sem_two);
        }
        printf("Result: %d \n", sum);
        return NULL;
    }

四.多線程併發服務端的實現

相關文章
相關標籤/搜索