POSIX 线程(一)

一. POSIX线程先关函数

POSIX线程库

与线程有关的函数构成一个完整的系列,绝大多数函数的名字都是以"pthread"打头的

要使用这些函数库,引入头文件<pthread.h>

连接这些线程函数库时要使用编译器命令的"-lpthread"选项

1. pthread_create函数

功能:创建一个新的线程

原型: int pthread_create(pthread_t *thread, const pthread_attr_t *attr,

void *(*start_routine) (void *), void *arg);

参数:

thread: 返回线程ID

attr: 设置线程的属性, attr为NULL 表示使用默认属性

start_routine: 是个函数地址,线程启动后要执行这个函数

arg : 传给线程启动后函数的参数

返回值:  成功返回0; 失败返回错误码

错误检查

(1) 传统一些函数是,成功返回0, 失败返回-1, 并且对全局变量 errno赋值以指示错误.

(2) pthreads 函数出错时不会设置全局变量 errno(而大部分其他POSIX函数会这样做).而是将错误代码通过返回值返回.

(3) pthreads 同样也提供了线程内的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);

参数:

thread: 线程ID

value_ptr : 它指向一个指针,后者指向线程的返回值

返回值: 成功返回0,失败返回错误码.

3. pthread_self 函数

功能: 返回线程ID

原型: pthread_t  pthread_self(void);

返回值: 成功返回0

4. pthread_cancel 函数

功能: 取消一个执行中的线程

原型:

int pthread_cancel(pthread_t  thread);

参数:

thread: 线程ID

返回值: 成功返回0, 失败返回错误码

5. pthread_detach 函数

功能: 将一个线程分离

原型:

int  pthread_detach(pthread_t thread);

参数:

thread: 线程ID

返回值: 成功返回0, 失败返回错误码

案例代码:

pthread.c

#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;
}

二. 用线程实现回射客户/服务器程序

#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);
        }
    }
}

时间: 2024-08-10 23:27:01

POSIX 线程(一)的相关文章

POSIX 线程详解(经典必看)

总共三部分: 第一部分:POSIX 线程详解                                   Daniel Robbins ([email protected]), 总裁/CEO, Gentoo Technologies, Inc.  2000 年 7 月 01 日 第二部分:通用线程:POSIX 线程详解,第 2部分       Daniel Robbins ([email protected]), 总裁/CEO, Gentoo Technologies, Inc.  20

POSIX 线程详解(1-概述)

线程是有趣的 线程类似于进程.如同进程,线程由内核按时间分片进行管理.在单处理器系统中,内核使用时间分片来模拟线程的并发执行,这种方式和进程的相同.而在多处理器系统中,如同多个进程,线程实际上一样可以并发执行. 那么为什么对于大多数合作性任务,多线程比多个独立的进程更优越呢?这是因为,线程共享相同的内存空间.不同的线程可以存取内存中的同一个变量.所以,程序中的所有线程都可以读或写声明过的全局变量.如果曾用 fork() 编写过重要代码,就会认识到这个工具的重要性.为什么呢?虽然 fork() 允

POSIX 线程详解(2-线程创建和销毁)

算法旨在用尽可能简单的思路解决问题,理解算法也应该是一个越看越简单的过程,当你看到算法里的一串概念,或者一大坨代码,第一感觉是复杂,此时不妨从例子入手,通过一个简单的例子,并编程实现,这个过程其实就可以理解清楚算法里的最重要的思想,之后扩展,对算法的引理或者更复杂的情况,对算法进行改进.最后,再考虑时间和空间复杂度的问题. 了解这个算法是源于在Network Alignment问题中,图论算法用得比较多,而对于alignment,特别是pairwise alignment, 又经常遇到maxim

POSIX线程(1)

POSIX线程库: 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以"pthread_"打头的 要使用这些函数库,要通过引入头文<pthread.h> 链接这些线程函数库时要使用编译器命令的"-lpthread"选项 pthread_create函数 功能:创建一个新的线程 原型 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_r

POSIX 线程编程(二)线程建立与终止

创建与终止线程 线程的管理常用的API有:pthread_create(thread,attr,start_routine,arg) pthread_exit(status) pthread_cancel(thread) pthread_attr_init(attr) pthread_attr_destroy(attr) 创建线程: 一个main程序包含一个默认的主线程,这个主线程在程序开始运行的时候由系统创建.除此之外的所有其他线程必须由程序员显式的创建. pthread_create 创建一

POSIX线程(2)

线程属性 初始化与销毁属性 int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr); 获取与设置分离属性 int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); int pthread_attr_setdetachstate(pthread_attr_t *attr,

Linux的POSIX线程属性

创建POSIX线程的函数为 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); 第1个参数为线程句柄(类似于文件描述符),第3个参数为线程启动函数(输入void*.返回void*,因为指向任何结构体/基本数据类型的指针都可以被看作void*,而void*一般都可以显式强制转换成指向对应类型的指针甚至整型,这是不支持纯C编程的常见

POSIX 线程详解(3-互斥量:&quot;固定加锁层次&quot;/“试加锁-回退”)

有时一个互斥量是不够的: 比如: 当多个线程同时访问一个队列结构时,你需要2个互斥量,一个用来保护队列头,一个用来保护队列元素内的数据. 当为多线程建立一个树结构时,你可能需要为每个节点设置一个互斥量. 同时使用多个互斥量会导致复杂度的增加 最坏的情况就是死锁的发生,即两个线程分别锁住一个互斥量而等待对方的互斥量. 多互斥量可能导致死锁: 如果可以在独立的数据上使用两个分离的互斥量,那么就应该这么做.这样,通过减少线程必须等待其他线程完成数据操作的时间. 如果数据独立,则某个特定函数就不太可能经

Posix线程编程指南(1)

Posix线程编程指南(1) 作者:杨沙洲 原文地址:http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part1/ 线程创建与取消 这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第一篇将向您讲述线程的创建与取消. 1 线程创建 1.1 线程与进程 相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有

Posix线程编程指南(5)

Posix线程编程指南(5) 杨沙洲 原文地址:http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part5/ 杂项 这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第五篇将向您讲述pthread_self().pthread_equal()和pthread_once()等杂项函数. 在Posix线程规范中还有几个辅助函数难以归类,暂且称其为杂项函数,主要包