与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”开头,要使用这些函数库,要通过引入头文
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
#include
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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) |