POSIX线程库复习

创建一个新的线程

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

thread:返回线程ID

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

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

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

 

成功返回0,失败返回错误码;

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>

#define ERR_EXIT(m) \
    do { \
        perror(m);\
        exit(EXIT_FAILURE);\
    }while(0)

void* therad_routine(void *arg)
{
    int i;
    for (i = 0; i < 20; ++i)
    {
        /* code */
        printf("B");
        fflush(stdout);
    }
    return 0;
}


int main(int argc, const char *argv[])
{
    pthread_t tid;
    int ret;
    ret = pthread_create(&tid, NULL, therad_routine, NULL);
    if(ret != 0)
    {
        fprintf(stderr, "pthread_create:%s\n", strerror(ret));\
        exit(EXIT_FAILURE);
    }
    int i;
    for (i = 0; i < 20; ++i)
    {
        /* code */
        printf("A");
        fflush(stdout);
        usleep(20);
    }
    sleep(1);
    return 0;
}
当线程创建失败时, 错误信息会返回到errno中。

pthread同样也提供了线程内的而errno变量,一支持其他使用errno的代码,

对于pthreads函数的错误,简易通过返回值判定,因为赌气返回值要比赌气线程内的errno变量的开销更小。

 

打印结果

ABABABABABAB

可以看得出单核的情况下,主线程和线程之间是共享CPU时间片的。

它们是交替进行的。

 

ret = pthread_join(tid, NULL);
    if(ret != 0)
    {
        fprintf(stderr, "pthread_join:%s\n", strerror(ret));
        exit(EXIT_FAILURE);
    }

主线程利用pthread_join来回收线程资源。

 

相对于多进程下僵尸进程的概念,多线程中也会产生僵尸线程。

所以我们可以使用pthread_detach来分离一个线程,这样便不会产生僵尸线程。

 

下面利用线程函数实现一个简单的回射服务器练习一下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define ERR_EXIT(m) \
    do { \
        perror(m);\
        exit(EXIT_FAILURE);\
    }while(0)

void* thread_routine(void* arg)
{
    pthread_detach(pthread_self());
    int conn = (int)arg;
    echo_sev(conn);
    printf("exiting thread ....\n");
    return 0;
}


int main(int argc, const char *argv[])
{
    int listen = socket(PF_INET, SOCK_STREAM, 0);
    if(socket < 0 )
        ERR_EXIT("socket");
    struct sockaddr_in servraddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(5188);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    int on = 1;
    //设置端口复用
    if(setsocketopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &ON, sizeof(on)) <0)
        ERR_EXIT("setsocketopt");
    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;
    int conn;
    while(1)
    {
        conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
        if(conn < 0)
            ERR_EXIT("accept");

        printf("ip = %s, port = %d\n", inet_ntoa(peeraddr.sin_addr, ntohs(peeraddr.sin_port)));
        pthread_t tid;
        int ret;
        ret = pthread_create(&tid, NULL, thread_routine, (void*)conn);
        if(ret != 0)
        {
            fprintf(stderr, "pthread_create:%s\n", strerrno(ret));
            exit(EXIT_FAILURE);
        }
    }

    
    return 0;
}

注意点就是,要将创建的线程进行detrach,来避免僵尸线程的产生。

 

二。线程属性

通过设置pthread_create的第二个参数来来达到我们的但的目的。

分离属性的获取与设置

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

这个函数有两个参数,

PTHREAD_CREATE_DETACHED:

          所以创建的线程都会是分离的。

PTHREAD_CREATE_JOINABLE:

所有新创建的线程都是不分离的。

 

实例代码如下

#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ERR_EXIT(m) \
    do { \
        perror(m);\
        exit(EXIT_FAILURE);\
    }while(0)

int main(int argc, const char *argv[])
{
    //先定义个线程属性变量并初始化
    pthread_attr_t attr;
    pthread_attr_init(&attr);
// 完成上面两部之后,attr中会保存各种线程的默认属性
    int state;
    pthread_attr_getdetachstate(&attr, &state);
    if(state == PTHREAD_CREATE_JOINABLE)
        printf("datechstate:PTHREAD_CRRATE_JOINABLE\n");
    else if(state == PTHREAD_CREATE_DETACHED)
        printf("datechstate:PTHREAD_CRRATE_DETACHED\n");

    size_t size;
    pthread_attr_getstacksize(&attr, &size);
    printf("stacksize:%d\n", size);

    pthread_attr_getguardsize(&attr, &size);
    printf("guardsize:%d\n", size);
    return 0;
}

可以通过上述的几个函数来获取线程的属性。

 

线程除了属性,还有128个特定的指针可以用于保存TSD即线程特有数据

需要手动自己create,可以指向任意的数据类型

#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ERR_EXIT(m) \
    do { \
        perror(m);\
        exit(EXIT_FAILURE);\
    }while(0)
typedef struct tsd
{
    pthreead_t tid;
    char *str;
}tsd_t;


pthread_key_t key_tsd;
void destroy_routine(void *value)
{
    printf("销毁。。。特定数据\n");
    free(value);
}

void thread_routine(void *value)
{
    tsd_t *value = (tsd_t*)malloc(sizeof(tsd_t));
    value->tid = pthread_self();
    value->str = (char*)arg;

    pthread_setspecific(key_tsd, value);
    printf("%ssetspecific%p\n", (char*)arg, value);
    value = pthread_getspecific(key_tsd);
    printf("tid=0x%x, str=%s\n", value->tid, value->str);
    sleep(2);


    return 0;
}
int main(int argc, const char *argv[])
{

    pthread_key_create(tsd, destroy_routine);

    pthread_t tid1;
    pthread_t tid2;
    pthread_create(&tid1, NULL, thread_routine, "thread1");
    pthread_create(&tid2, NULL, thread_routine, "thread2");

    pthread_join(&tid1, NULL);
    pthread_join(&tid2, NULL);


    pthread_key_delete(dkey_tsd);
    return 0;
}

解析:

create函数会寻找一个数据空位。

pthread_key_create(tsd, destroy_routine);

线程退出后会自动调用destroy_routine指向的函数去销毁该TSD。

pthread_setspecific(key_tsd, value);

这个函数时将要保存的信息保存到放入我们申请的那个TSD中。

    value = pthread_getspecific(key_tsd);

这个函数可以从TSD中获取到之前保存的数据。

posted @ 2015-03-14 14:47  tilly_chang  阅读(204)  评论(0编辑  收藏  举报