Linux下多線程編程詳解

來源:互聯網
上載者:User
線程(thread)技術早在60年代就被提出,但真正應用多線程到作業系統中去,是在80年代中期,solaris是這方面的佼佼者。傳統的Unix也支援線程的概念,但是在一個進程(process)中只允許有一個線程,這樣多線程就意味著多進程。
現在,多線程技術已經被許多作業系統所支援,包括Windows/NT,當然,也包括Linux。

  為什麼有了進程的概念後,還要再引入線程呢?使用多線程到底有哪些好處?什麼的系統應該選用多線程?我們首先必須回答這些問題。

  使用多線程的理由之一是和進程相比,它是一種非常"節儉"的多任務操作方式。我們知道,在Linux系統下,啟動一個新的進程必須分配給它獨立的地址空間,建立眾多的資料表來維護它的程式碼片段、堆棧段和資料區段,這是一種"昂貴"的多任務工作方式。而運行於一個進程中的多個線程,它們彼此之間使用相同的地址空間,共用大部分資料,啟動一個線程所花費的空間遠遠小於啟動一個進程所花費的空間,而且,線程間彼此切換所需的時間也遠遠小於進程間切換所需要的時間。據統計,總的說來,一個進程的開銷大約是一個線程開銷的30倍左右,當然,在具體的系統上,這個資料可能會有較大的區別。

  使用多線程的理由之二是線程間方便的通訊機制。對不同進程來說,它們具有獨立的資料空間,要進行資料的傳遞只能通過通訊的方式進行,這種方式不僅費時,而且很不方便。線程則不然,由於同一進程下的線程之間共用資料空間,所以一個線程的資料可以直接為其它線程所用,這不僅快捷,而且方便。當然,資料的共用也帶來其他一些問題,有的變數不能同時被兩個線程所修改,有的子程式中聲明為static的資料更有可能給多線程程式帶來災難性的打擊,這些正是編寫多線程程式時最需要注意的地方。

  除了以上所說的優點外,不和進程比較,多線程程式作為一種多任務、並發的工作方式,當然有以下的優點:

  1) 提高應用程式響應。這對圖形介面的程式尤其有意義,當一個操作耗時很長時,整個系統都會等待這個操作,此時程式不會響應鍵盤、滑鼠、菜單的操作,而使用多線程技術,將耗時間長度的操作(time consuming)置於一個新的線程,可以避免這種尷尬的情況。

  2) 使多CPU系統更加有效。作業系統會保證當線程數不大於CPU數目時,不同的線程運行於不同的CPU上。

  3) 改善程式結構。一個既長又複雜的進程可以考慮分為多個線程,成為幾個獨立或半獨立的運行部分,這樣的程式會利於理解和修改。

  下面我們先來嘗試編寫一個簡單的多線程程式。

  簡單的多線程編程

  Linux系統下的多線程遵循POSIX線程介面,稱為pthread。編寫Linux下的多線程程式,需要使用標頭檔pthread.h,串連時需要使用庫libpthread.a。順便說一下,Linux下pthread的實現是通過系統調用clone()來實現的。clone()是Linux所特有的系統調用,它的使用方式類似fork,關於clone()的詳細情況,有興趣的讀者可以去查看有關文檔說明。下面我們展示一個最簡單的多線程程式 pthread_create.c。

一個重要的線程建立函數原型:
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr, void *(*start_rtn)(void),void *restrict arg);

    傳回值:若是成功建立線程返回0,否則返回錯誤的編號
    形式參數:
                pthread_t *restrict tidp 要建立的線程的線程id指標
                const pthread_attr_t *restrict attr 建立線程時的線程屬性
                void* (start_rtn)(void) 傳回值是void類型的指標函數
                void *restrict arg   start_rtn的行參
                
常式1:                               
    功能:建立一個簡單的線程
    程式名稱:pthread_create.c         
/********************************************************************************************
**    Name:pthread_create.c
**    Used to study the multithread programming in Linux OS
**    Author:zeickey
**    Date:2006/9/16       
**    Copyright (c) 2006,All Rights Reserved!
*********************************************************************************************/

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

void *myThread1(void)
{
    int i;
    for (i=0; i<100; i++)
    {
        printf("This is the 1st pthread,created by zieckey./n");
        sleep(1);//Let this thread to sleep 1 second,and then continue to run
    }
}

void *myThread2(void)
{
    int i;
    for (i=0; i<100; i++)
    {
        printf("This is the 2st pthread,created by zieckey./n");
        sleep(1);
    }
}

int main()
{
    int i=0, ret=0;
    pthread_t id1,id2;
   
    ret = pthread_create(&id2, NULL, (void*)myThread1, NULL);
    if (ret)
    {
        printf("Create pthread error!/n");
        return 1;
    }
   
    ret = pthread_create(&id2, NULL, (void*)myThread2, NULL);
    if (ret)
    {
        printf("Create pthread error!/n");
        return 1;
    }
   
    pthread_join(id1, NULL);
    pthread_join(id2, NULL);
   
    return 0;
}

  我們編譯此程式:
# gcc pthread_create.c -lpthread

因為pthread的庫不是linux系統的庫,所以在進行編譯的時候要加上-lpthread,否則編譯不過,會出現下面錯誤
thread_test.c: 在函數 ‘create’ 中:
thread_test.c:7: 警告: 在有傳回值的函數中,程式流程到達函數尾
/tmp/ccOBJmuD.o: In function `main':thread_test.c:(.text+0x4f):對‘pthread_create’未定義的引用
collect2: ld 返回 1

  運行,我們得到如下結果:
# ./a.out
This is the 1st pthread,created by zieckey.
This is the 2st pthread,created by zieckey.
This is the 1st pthread,created by zieckey.
This is the 2st pthread,created by zieckey.
This is the 2st pthread,created by zieckey.
This is the 1st pthread,created by zieckey.
....

兩個線程交替執行。
此例子介紹了建立線程的方法。
下面例子介紹向線程傳遞參數。

常式2:
    功能:向新的線程傳遞整形值
    程式名稱:pthread_int.c
/********************************************************************************************
**    Name:pthread_int.c
**    Used to study the multithread programming in Linux OS
**    Pass a parameter to the thread.
**    Author:zeickey
**    Date:2006/9/16       
**    Copyright (c) 2006,All Rights Reserved!
*********************************************************************************************/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *create(void *arg)
{
    int *num;
    num=(int *)arg;
    printf("create parameter is %d /n",*num);
    return (void *)0;
}
int main(int argc ,char *argv[])
{
    pthread_t tidp;
    int error;
   
    int test=4;
    int *attr=&test;
   
    error=pthread_create(&tidp,NULL,create,(void *)attr);
    if(error)
        {
        printf("pthread_create is created is not created ... /n");
        return -1;
        }
    sleep(1);
    printf("pthread_create is created .../n");
    return 0;       
}
    編譯方法:

gcc -lpthread pthread_int.c -Wall
    執行結果:

create parameter is 4
pthread_create is created is  created ...

    常式總結:
    可以看出來,我們在main函數中傳遞的整行指標,傳遞到我們建立的線程函數中。
    在上面的例子可以看出來我們向新的線程傳入了另一個線程的int資料,線程之間還可以傳遞字串或是更複雜的資料結構。

常式3:
    程式功能:向建立的線程傳遞字串
        程式名稱:pthread_string.c
/********************************************************************************************
**    Name:pthread_string.c
**    Used to study the multithread programming in Linux OS
**    Pass a ‘char*‘ parameter to the thread.
**    Author:zeickey
**    Date:2006/9/16       
**    Copyright (c) 2006,All Rights Reserved!
*********************************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

void *create(void *arg)
{
    char *name;
    name=(char *)arg;
    printf("The parameter passed from main function is %s  /n",name);
    return (void *)0;
}

int main(int argc, char *argv[])
{
    char *a="zieckey";
    int error;
    pthread_t tidp;

    error=pthread_create(&tidp, NULL, create, (void *)a);
    if(error!=0)
    {
        printf("pthread is not created./n");
        return -1;
    }
    sleep(1);
    printf("pthread is created... /n");
    return 0;
}   

  編譯方法:
gcc -Wall pthread_string.c -lpthread

    執行結果:
The parameter passed from main function is zieckey 
pthread is created...

    常式總結:
    可以看出來main函數中的字串傳入了建立的線程中。

常式4:
    程式功能:向建立的線程傳遞字串
        程式名稱:pthread_struct.c
/********************************************************************************************
**    Name:pthread_struct.c
**    Used to study the multithread programming in Linux OS
**    Pass a ‘char*‘ parameter to the thread.
**    Author:zeickey
**    Date:2006/9/16       
**    Copyright (c) 2006,All Rights Reserved!
*********************************************************************************************/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

struct menber
{
    int a;
    char *s;
};

void *create(void *arg)
{
    struct menber *temp;
    temp=(struct menber *)arg;
    printf("menber->a = %d  /n",temp->a);
    printf("menber->s = %s  /n",temp->s);
    return (void *)0;
}

int main(int argc,char *argv[])
{
    pthread_t tidp;
    int error;
    struct menber *b;
    b=(struct menber *)malloc( sizeof(struct menber) );
    b->a = 4;
    b->s = "zieckey";

    error = pthread_create(&tidp, NULL, create, (void *)b);
    if( error )
    {
        printf("phread is not created.../n");
        return -1;
    }
    sleep(1);
    printf("pthread is created.../n");
    return 0;
}

  編譯方法:

gcc -Wall pthread_struct.c -lpthread

    執行結果:
menber->a = 4 
menber->s = zieckey 
pthread is created...

    常式總結:
    可以看出來main函數中的一個結構體傳入了建立的線程中。
    線程包含了標識進程內執行環境必須的資訊。他整合了進程中的所有資訊都是對線程進行共用的,包括文本程式、程式的全域記憶體和堆記憶體、棧以及檔案描述符。
   

常式5:
    程式目的:驗證建立立的線程可以共用進程中的資料
    程式名稱:pthread_share.c 

/********************************************************************************************
**    Name:pthread_share_data.c
**    Used to study the multithread programming in Linux OS
**    Pass a ‘char*‘ parameter to the thread.
**    Author:zeickey
**    Date:2006/9/16       
**    Copyright (c) 2006,All Rights Reserved!
*********************************************************************************************/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

static int a=4;
void *create(void *arg)
{
    printf("new pthread ... /n");
    printf("a=%d  /n",a);
    return (void *)0;
}

int main(int argc,char *argv[])
{
    pthread_t tidp;
    int error;
   
    a=5;
    error=pthread_create(&tidp, NULL, create, NULL);
    if(error!=0)
    {
        printf("new thread is not create ... /n");
        return -1;
    }
   
    sleep(1);   
    printf("new thread is created ... /n");
    return 0;
}
   
  編譯方法:
gcc -Wall pthread_share_data.c -lpthread
    執行結果:
new pthread ...
a=5 
new thread is created ... 
    常式總結:
可以看出來,我們在主線程更改了我們的全域變數a的值的時候,我們建立立的線程則列印出來了改變的值,可以看出可以訪問線程所在進程中的資料資訊。
 2、線程的終止
    如果進程中任何一個線程中調用exit,_Exit,或者是_exit,那麼整個進程就會終止,
    與此類似,如果訊號的預設的動作是終止進程,那麼,把該訊號發送到線程會終止進程。
    線程的正常退出的方式:
       (1) 線程只是從啟動常式中返回,傳回值是線程中的退出碼
       (2) 線程可以被另一個進程進行終止
       (3) 線程自己調用pthread_exit函數
    兩個重要的函數原型:

#include <pthread.h>
void pthread_exit(void *rval_ptr);
/*rval_ptr 線程退出返回的指標*/

int pthread_join(pthread_t thread,void **rval_ptr);
   /*成功結束進程為0,否則為錯誤編碼*/

    常式6
    程式目的:線程正常退出,接受線程退出的返回碼
    程式名稱:pthread_exit.c

/********************************************************************************************
**    Name:pthread_exit.c
**    Used to study the multithread programming in Linux OS
**    A example showing a thread to exit and with a return code.
**    Author:zeickey
**    Date:2006/9/16        
**    Copyright (c) 2006,All Rights Reserved!
*********************************************************************************************/

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

void *create(void *arg)
{
    printf("new thread is created ... /n");
    return (void *)8;
}

int main(int argc,char *argv[])
{
    pthread_t tid;
    int error;
    void *temp;

    error = pthread_create(&tid, NULL, create, NULL);
    if( error )
    {
        printf("thread is not created ... /n");
        return -1;
    }
    error = pthread_join(tid, &temp);
    if( error )
    {
        printf("thread is not exit ... /n");
        return -2;
    }
    
    printf("thread is exit code %d /n", (int )temp);
    return 0;
}

  編譯方法:
gcc -Wall pthread_exit.c -lpthread
    執行結果:
new thread is created ...
thread is exit code 8

    常式總結:
可以看出來,線程退出可以返回線程的int數值。線程退出不僅僅可以返回線程的int數值,還可以返回一個複雜的資料結構。

    常式7
    程式目的:線程結束返回一個複雜的資料結構
    程式名稱:pthread_return_struct.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

struct menber
{
    int a;
    char *b;
}temp={8,"zieckey"};
void *create(void *arg)
{
    printf("new thread ... /n");
    return (void *)&temp;
}

int main(int argc,char *argv[])
{
    int error;
    pthread_t tid;
    struct menber *c;

    error = pthread_create(&tid, NULL, create, NULL);
    if( error )
    {
        printf("new thread is not created ... /n");
        return -1;
    }
    printf("main ... /n");
    error = pthread_join(tid,(void *)&c);
    if( error )
    {
        printf("new thread is not exit ... /n");
        return -2;
    }
    printf("c->a = %d  /n",c->a);
    printf("c->b = %s  /n",c->b);
    sleep(1);
    return 0;
}

  編譯方法:
gcc -Wall pthread_return_struct.c -lpthread

    執行結果:
main ...
new thread ...
c->a = 8
c->b = zieckey

常式總結:
一定要記得返回的資料結構要是在這個資料要返回的結構沒有釋放的時候應用,
如果資料結構已經發生變化,那返回的就不會是我們所需要的,而是髒資料
3、線程標識
    函數原型:
   
#include <pthread.h>
pthread_t pthread_self(void);

pid_t getpid(void);
    getpid()用來取得目前進程的進程識別碼,函數說明

    常式8
    程式目的:實現在建立立的線程中列印該線程的id和進程id
    程式名稱:pthread_id.c
  
/********************************************************************************************
**    Name:pthread_id.c
**    Used to study the multithread programming in Linux OS.
**    Showing how to get the thread's tid and the process's pid.
**    Author:zeickey
**    Date:2006/9/16       
**    Copyright (c) 2006,All Rights Reserved!
*********************************************************************************************/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> /*getpid()*/

void *create(void *arg)
{
    printf("New thread .... /n");
    printf("This thread's id is %u  /n", (unsigned int)pthread_self());
    printf("The process pid is %d  /n",getpid());
    return (void *)0;
}

int main(int argc,char *argv[])
{
    pthread_t tid;
    int error;

    printf("Main thread is starting ... /n");
    error = pthread_create(&tid, NULL, create, NULL);
    if(error)
    {
        printf("thread is not created ... /n");
        return -1;
    }
    printf("The main process's pid is %d  /n",getpid());
    sleep(1);
    return 0;
}

    編譯方法:
gcc -Wall -lpthread pthread_id.c

    執行結果:
Main thread is starting ...
The main process's pid is 3307 
New thread ....
This thread's id is 3086347152 
The process pid is 3307  發表於 2007-10-30 09:04 zz 閱讀(1141) 評論(1)  編輯 收藏 引用 所屬分類: linux c編程  

評論

# re: Linux下多線程編程詳解[未登入] ret = pthread_create(&id2, NULL, (void*)myThread1, NULL);
if (ret)
{
printf("Create pthread error!/n");
return 1;
}

例1中代碼有錯誤。
ret = pthread_create(&id2, NULL, (void*)myThread1, NULL);
應該為
ret = pthread_create(&id1, NULL, (void*)myThread1, NULL);

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.