1. 程式人生 > >淺談一下linux線程

淺談一下linux線程

順序 分享圖片 art 釋放資源 默認 pre 線程互斥 array []

  1.線程是進程中最小執行單元,多線程共享同一個進程的地址空間
  2.Linux 內核調度的對象是線程,所以一個進程中多個線程參與操作操作系統統一調度

  使用線程優點:
    <1>效率高
    <2>線程之間通信比較簡單(全局變量)

使用線程缺點:
  安全性差

線程API

  1.線程創建
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
    void *(*start_routine) (void *), void *arg);

    參數:
      @thread 獲取線程ID
      @attr 設置線程屬性 NULL:默認屬性
      @start_routine 線程執行的函數
      @arg 給線程執行函數傳遞的參數

返回值:
  成功返回0, 失敗返回錯誤碼

實例如下:

#include <head.h>

void *output(void *arg)
{
    int data = *(int *)arg;
    printf("%d : %d\n",pthread_self(),data);
    return;
}

int main(int argc, const char *argv[])
{
    int ret;
    int count = 0;
    int data = 0;
    pthread_t tid;

    while(1)
    {
        
/*bug : * 一個線程被創建後,不一定立即運行,而每個線程都是從data的地址中讀取數據 *,而data地址的數據又一直在發生變化,這樣可能會導致,線程運行的時候,從data地址讀取 *的數據已經不是它想要的數據了。 * */ ret = pthread_create(&tid,NULL,output,&data); if(ret != 0){ fprintf(stderr,"Fail to pthread_create : %s\n
",strerror(ret)); exit(EXIT_FAILURE); } i ++; pthread_detach(tid); data ++; count ++; printf("count = %d!\n",count); // pthread_join(tid,NULL); } return 0; }

運行如下

技術分享圖片

實例如下
創建兩個子線程,子1線程輸出data值,子2線程data ++操作,主線程將data -- 操作 (data 在主線程定義)

#include <head.h>

void *output_data(void *arg)
{
    int data = *(int *)arg;
    
    while(1)
    {
        data = *(int *)arg;
        printf("data = %d!\n",data);
//        sleep(1);    
    }
}

void *add_data(void *arg)
{
    int *pdata = (int *)arg;
    while(1)
    {
        (*pdata) ++;
    }
}

int main(int argc, const char *argv[])
{
    int ret;
    pthread_t tid1,tid2;
    int data = 10;

    ret = pthread_create(&tid1,NULL,output_data,&data);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_create : %s\n",strerror(ret));
        exit(EXIT_FAILURE);
    }
    
    ret = pthread_create(&tid2,NULL,add_data,&data);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_create : %s\n",strerror(ret));
        exit(EXIT_FAILURE);
    }

    while(1)
    {
        data --;
    }
    
    exit(EXIT_SUCCESS);
}

運行結果

技術分享圖片

可見線程的運行並沒有先後順序

2.線程退出

(1)線程函數返回 (return)
(2)pthread_exit
(3)pthread_cancel
(4)進程結束,這個進程中所有的線程都退出

void pthread_exit(void *retval);
功能:用來退出一個線程
參數:
@retval 返回一個地址值


int pthread_join(pthread_t thread, void **retval);
功能:等待一個線程退出,將退出線程未釋放的資源釋放掉
參數:
@thread 需要等待退出線程的ID
@retval 獲得線程退出返回的值


void *thread_function(void *arg)
{
int data = 100;

pthread_exit(&data);
}

int main()
{
int *p;

pthread_join(tid,&p);
return 0;
}

3.將線程標記分離

分離狀態的線程在結束的時候,系統自動回收它的資源

int pthread_detach(pthread_t thread);
@thread 線程ID


實例如下:
  (1)全局int data[] = {1,2,3,4,5,6,7,8,9,10,11};
  (2)線程1一直對全局數組做逆置
  (3)線程2一直對全局數組輸出

#include <head.h>

//pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock;

int a[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};

void *output_array(void *arg)
{
    int i = 0;
    
    //實現效果:如果發現逆置操作沒有結束,則不輸出此時阻塞自己
    while(1)
    {    
        pthread_mutex_lock(&lock);
        for(i = 0;i < 20;i ++){
            printf("%d ",a[i]);
        }
        printf("\n");
        pthread_mutex_unlock(&lock);
    
    }
}

void *reverse_array(void *arg)
{
    int t;
    int i ,j;
    
    //實現效果:如果輸出操作沒有結束,則不逆置阻塞字節
    while(1)
    {
        pthread_mutex_lock(&lock);
        i = 0;
        j = 19;

        while(i < j){
            t = a[i];
            a[i] = a[j];
            a[j] = t;

            i ++;
            j --;
        }
        pthread_mutex_unlock(&lock);
    }
}

int main(int argc, const char *argv[])
{
    int ret;
    pthread_t tid1,tid2;
    int data = 10;

    ret = pthread_mutex_init(&lock,NULL);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_mutex_init");
        exit(EXIT_FAILURE);
    }

    ret = pthread_create(&tid1,NULL,output_array,NULL);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_create : %s\n",strerror(ret));
        exit(EXIT_FAILURE);
    }
    
    ret = pthread_create(&tid2,NULL,reverse_array,NULL);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_create : %s\n",strerror(ret));
        exit(EXIT_FAILURE);
    }

    pthread_join(tid1,NULL);
    
    exit(EXIT_SUCCESS);
}

運行結果

技術分享圖片

並沒有出現還沒有逆置完成就輸出的

但如果去掉

pthread_mutex_lock(&lock);

pthread_mutex_unlock(&lock);

呢?

線程互斥鎖

  功能:對共享資源實現互斥訪問,保證訪問的完整性

  如果使用互斥鎖,每個線程在訪問共享資源,都必須先獲得互斥鎖,然後在訪問共享資源,如果無法獲得互斥鎖
  ,則表示有其他線程正在訪問共享資源,此時沒有獲得鎖的線程會阻塞,直到其他線程釋放鎖,能再次獲得鎖

1.定義互斥鎖[全局,讓每個線程都可以訪問到]
pthread_mutex_t lock;

2.初始化互斥鎖

//[線程創建之前]動態初始化,屬性使用默認 NULL
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

//靜態初始化定義初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

3.獲得互斥鎖
int pthread_mutex_lock(pthread_mutex_t *mutex);//操作共享資源之前加鎖
int pthread_mutex_trylock(pthread_mutex_t *mutex);

4.釋放鎖
int pthread_mutex_unlock(pthread_mutex_t *mutex);//操作共享資源結束的時候

5.銷毀鎖
int pthread_mutex_destroy(pthread_mutex_t *mutex);//不需要再次使用鎖的時候

線程間同步

  同步:相互之間配合完成一件事情
  互斥:保證訪問共享資源的完整性(有你沒我)

  POSIX 線程中同步:使用信號量實現

  信號量 : 表示一類資源,它的值表示資源的個數

對資源訪問:
  p操作(申請資源) [將資源的值 - 1]
  ....
  V操作(釋放資源) [將資源的值 + 1]

1.定義信號量
sem_t sem ;

2.初始化信號量
int sem_init(sem_t *sem, int pshared, unsigned int value);
參數:
  @sem 信號量
  @pshared 0:線程間使用
  @value 初始化的信號量的值
返回值:
  成功返回0,失敗返回-1

3.P操作
  int sem_wait(sem_t *sem);

4.V操作
  int sem_post(sem_t *sem);

實例如下:
創建兩個線程
write_thread : 從鍵盤上讀取用戶輸入的數據,寫到文件中
read_thread : 從文件中讀取數據,然後輸出
main_thread : 打開一個文件

#include <head.h>

sem_t read_sem;//讀資源
sem_t write_sem;//寫資源


void *write_thread(void *arg)
{
    char buf[1024];
    int fd = *(int *)arg;

    while(1){
        //申請寫資源
        if(sem_wait(&write_sem) < 0){
            perror("Fail to sem_wait");
            pthread_exit(NULL);
        }

        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf) - 1] = \0;

        write(fd,buf,strlen(buf));

        //還原偏移量到上一次值
        lseek(fd,-strlen(buf),SEEK_CUR);

        //釋放讀資源
        if(sem_post(&read_sem) < 0){
            perror("Fail to sem_wait");
            pthread_exit(NULL);
        }

        if(strcmp(buf,"quit") == 0){
            break;
        }

    }

    pthread_exit(NULL);
}

void *read_thread(void *arg)
{
    int n;
    char buf[1024];
    int fd = *(int *)arg;

    while(1){
        //申請讀資源
        if(sem_wait(&read_sem) < 0){
            perror("Fail to sem_wait");
            pthread_exit(NULL);
        }

        n = read(fd,buf,sizeof(buf));
        buf[n] = \0;
        printf("Read %d bytes : %s!\n",n,buf);

        //釋放寫資源
        if(sem_post(&write_sem) < 0){
            perror("Fail to sem_wait");
            pthread_exit(NULL);
        }

        if(strcmp(buf,"quit") == 0){
            break;
        }
    }

    pthread_exit(NULL);
}

//./a.out  file
int main(int argc, const char *argv[])
{
    int fd;
    int ret;
    pthread_t rtid;
    pthread_t wtid;

    if(argc < 2){
        fprintf(stderr,"Usage : %s <filename>!\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    fd = open(argv[1],O_RDWR | O_CREAT | O_TRUNC,0666);
    if(fd < 0){
        fprintf(stderr,"Fail to open %s : %s\n",argv[1],strerror(errno));
        exit(EXIT_FAILURE);
    }

    if(sem_init(&read_sem,0,0) < 0){
        perror("Fail to sem_init");
        exit(EXIT_FAILURE);
    }

    if(sem_init(&write_sem,0,1) < 0){
        perror("Fail to sem_init");
        exit(EXIT_FAILURE);
    }

    ret = pthread_create(&rtid,NULL,read_thread,&fd);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_create : %s!\n",strerror(ret));
        exit(EXIT_FAILURE);
    }

    ret = pthread_create(&wtid,NULL,write_thread,&fd);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_create : %s!\n",strerror(ret));
        exit(EXIT_FAILURE);
    }
    
    pthread_join(rtid,NULL);
    pthread_join(wtid,NULL);

    return 0;
}

淺談一下linux線程