Linux多線程 --線程基本API

POSIX線程庫

  與線程有關的函數構成了一個完整的系列,絕大多數函數的名字都是以“pthread_”開頭,要使用這些函數庫,要通過引入頭文<pthread.h>,而且鏈接這些線程函數庫時要使用編譯器命令的“-lpthread”選項[Ubuntu系列系統需要添加的是”-pthread”選項而不是”-lpthread”,如Ubuntu 14.04版本]

 

1.pthread_create

int pthread_create(pthread_t *restrict thread,
		const pthread_attr_t *restrict attr,
		void *(*start_routine)(void*), void *restrict arg);

創建一個新的線程

參數

  thread:線程ID

  attr:設置線程的屬性,一般設置爲NULL表示使用默認屬性

  start_routine:是個函數地址,線程啓動後要執行的函數

  arg:傳給線程啓動函數的參數

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

錯誤檢查

  UNIX傳統的函數:成功返回0,失敗返回-1,並且對設置全局變量errno以指定錯誤類型。然而pthreads函數出錯時不會設置全局變量errno(而其他的大部分POSIX函數會設置errno)。而是將錯誤代碼通過返回值返回;

  pthreads同樣也提供了線程內的errno變量,對於每一個線程, 都有一個errno的值, 以支持其它使用errno的代碼。對於pthreads函數的錯誤,建議通過返回值進行判定,因爲讀取返回值要比讀取線程內的errno變量的開銷更小!

2.pthread_exit

void pthread_exit(void *value_ptr);

線程終止

  value_ptr:指向該線程的返回值;注意:value_ptr不能指向一個局部變量。

3.pthread_join

int pthread_join(pthread_t thread, void **value_ptr);

等待線程結束

  value_ptr:它指向一個指針,後者指向線程的返回值(用戶獲取線程的返回值)

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

#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
 
 
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
 
#define ERR_EXIT(m) \
	do \
	{ \
		perror(m); \
		exit(EXIT_FAILURE); \
	}while(0)
	
	
 
void* thread_routine(void *arg)
{
	int i;
	for(i=0;i<20;i++)
	{
		printf(" B ");
		fflush(stdout);
		usleep(20);
		
		if(i==3)
			pthread_exit("when i==3, pthread exit ");
	}
	sleep(3);  //延遲子線程的結束 
	return 0;
}
 
int main()
{
	pthread_t tid;
	int ret;
	// 錯誤信息通過函數返回
	if ( (ret = pthread_create(&tid,NULL,thread_routine,NULL)) !=0 ) 
	{
	 	fprintf(stderr,"pthread_create:%s\n",strerror(ret));
	 	exit(EXIT_FAILURE);
	}
	int i;   /// 爲主線程,打印字母 A 
	for(i=0;i<20;++i)
	{
		printf(" A ");
		fflush(stdout);	// 刷新輸出緩衝區
		usleep(20);
	}
	
	// 等待子線程的結束 
	void *value;
	if( (ret = pthread_join(tid,&value)) != 0)
	{
		fprintf(stderr,"pthread_create:%s\n",strerror(ret));
		exit(EXIT_FAILURE);
	}
	printf("\n");
	printf("return message: %s\n",(char*)value);
	return 0;
}

在線程中同樣存在殭屍線程,子線程退出,主線程沒有pthread_join等待。將線程設置爲脫離狀態可以避免殭屍線程

4.pthread_self

pthread_t pthread_self(void);

返回線程ID

 

5.pthread_cancel 
 取消一個執行中的線程,由其他線程執行,屬於線程的它殺

    int pthread_cancel(pthread_t  thread);

參數:
    thread: 線程ID
返回值: 成功返回0, 失敗返回錯誤碼



6. pthread_detach 函數
 將一個線程分離,避免殭屍線程

    int  pthread_detach(pthread_t thread);

參數:
    thread: 線程ID

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

 

 

例:用線程實現回射客戶/服務器程序

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
 
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
 
#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        } while(0)
 
void echo_srv(int conn)
{
    char recvbuf[1024];
    while (1)
    {
        memset(recvbuf, 0, sizeof(recvbuf));
        int ret = read(conn, recvbuf, sizeof(recvbuf));
        if (ret == 0)
        {
            printf("client close\n");
            break;
        }
        else if (ret == -1)
            ERR_EXIT("read");
        fputs(recvbuf, stdout);
        write(conn, recvbuf, ret);
    }
   close(conn);
}
 
void *thread_routine(void *arg)
{
     // 主線程沒有調用pthread_join等待線程退出
     //剝離線程,避免產生僵線程    int conn = (int)arg;
     // pthread_self 返回線程ID
     // pthread_detach 分離線程
    pthread_detach(pthread_self());
    int conn = *((int *)arg);  // 將無類型指針強制轉換成int* 指針
    free(arg); // 取完值,free掉
    echo_srv(conn); //每個線程處理一個連接,同一個進程沒有可監聽套接字
    printf("exiting thread ...\n");
    return NULL;
}
 
int main(void)
{
    int listenfd;
    if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        ERR_EXIT("socket");
 
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(5188);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 
    int on = 1;
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
        ERR_EXIT("setsockopt");
 
    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
        ERR_EXIT("bind");
    if (listen(listenfd, SOMAXCONN) < 0)
        ERR_EXIT("listen");
 
    struct sockaddr_in peeraddr;
    socklen_t peerlen = sizeof(peeraddr);
    int conn;
 
    while (1)
    {
        if ((conn = accept(listenfd, (struct sockaddr *)&peeraddr, &peerlen)) < 0)
            ERR_EXIT("accept");
 
        printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
 
        pthread_t tid;
        //int ret;  /*pthread_create(&tid, NULL, thread_routine, (void*)&conn);*/
        // race condition問題,竟態問題        
        int *p = malloc(sizeof(int));
        *p = conn;
        int ret;
        if ((ret = pthread_create(&tid, NULL, thread_routine,p)) != 0) 
        //64位系統時指針不是4個字節,不可移植 , 所有使用malloc,               
        {   
        	fprintf(stderr, "pthread_create:%s\n", strerror(ret));                     
            exit(EXIT_FAILURE);                
        }    
    }
}

進程 VS. 線程

進程(pid_t)

線程(pthread_t)

Fork

Pthread_create

Waitpit

Pthread_join/Pthread_detach

Kill

Pthread_cancel

getpid

Pthead_self

Exit/return

Pthread_exit/return

殭屍進程(沒有調用wait/waitpid等函數)

殭屍線程(沒有調用pthread_join/pthread_detach)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章