POSIX多线程程序设计(第5章:线程高级编程)

1 一次性初始化

    对一些Posix变量,如互斥量,我们可以静态地初始化pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;当不能静态地初始化一个互斥量时,只好动态初始化pthread_mutex_init。这样,问题就来了。在使用多线程时,如果多个线程并发地执行动态初始化时,该互斥量就会被初始化多次,而该过程本该仅仅执行一次。故而,对一些不该被多次初始化的Posix变量,如互斥量、线程键等,执行一次性初始化是最好的选择。

1.1函数

头文件

#include <pthread.h>

函数

int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));

参数

once_control:控制变量,必须静态初始化

pthread_once_t once_control = PTHREAD_ONCE_INIT;

init_routine:初始化函数

返回

成功返回0,出错返回错误码

功能

pthread_once函数首先检查控制变量,以判断是否已经完成初始化。如果完成,pthread_once简单地返回;否则, pthread_once调用初始化函数init_routine,并且记录初始化被完成。如果一个线程初始化时,另外的线程调用Pthread_once,则调用线程将等待,直到那个线程完成初始化后返回。

1.2实例(以互斥量为例,线程键例子第4节)

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

pthread_once_t once_control = PTHREAD_ONCE_INIT;/*控制变量*/
pthread_mutex_t mutex;

/*初始化函数*/
void once_init_routine(void)
{
    pthread_mutex_init(&mutex, NULL);
}

void *thread_routine(void *arg)
{
    int ret;
    
    /*一次初始化互斥量mutex*/
    ret = pthread_once(&once_control, once_init_routine);
    if (ret != 0)
    {
        printf("Once init eror: %s\n", strerror(ret));
        pthread_exit((void *)0);
    }
    
    pthread_mutex_lock(&mutex);
    printf("thread routine has locked the mutex\n");
    pthread_mutex_unlock(&mutex);

    return NULL;
}

int main(int argc, char *argv[])
{
    int status;
    pthread_t thread_id;
    
    status = pthread_create(&thread_id, NULL, thread_routine, NULL);
    if (status != 0)
    {
        printf("pthread_create eror: %s\n", strerror(status));
        pthread_exit((void *)1);
    }

    status = pthread_once(&once_control, once_init_routine);
    if (status != 0)
    {
        printf("Once init eror: %s\n", strerror(status));
        pthread_exit((void *)1);
    }

    pthread_mutex_lock(&mutex);
    printf("main has locked the mutex.\n");
    pthread_mutex_unlock(&mutex);


    pthread_join(thread_id, NULL);

    return 0;
    
}

2 属性

2.1互斥量属性

        POSIX为互斥量定义了3个属性:psharedprotocolprioceiling,其中pshared属性默认为PTHREAD_PROCESS_PRIVATE,也可设置为PTHREAD_PROCESS_SHARED,这样就能使用互斥量在多个进程间同步线程。

pthread_mutexattr_t attr;/*互斥量属性*/

/*初始化互斥量属性为默认值*/

int pthread_mutexattr_init(pthread_mutexattr_t *attr);

/*销毁互斥量属性对象*/

int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);

/*设置互斥量共享属性*/

int pthread_mutexattr_setpshard(pthread_mutexattr_t *attr, int pshared);

psharedPTHREAD_PROCESS_PRIVATEPTHREAD_PROCESS_SHARED

/*获取互斥量共享属性*/

int pthread_mutexattr_getpshard(pthread_mutexattr_t *attr, int *pshared);

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

pthread_mutex_t mutex;

int main(int argc, char *argv[])
{
    int ret;
    pthread_mutexattr_t mutex_attr;

    pthread_mutexattr_init(&mutex_attr);

    ret = pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_PRIVATE);
    if (ret != 0)
    {
        printf("mutex setpshared error : %s\n", strerror(ret));
        pthread_exit((void *)0);
    }

    pthread_mutex_init(&mutex, &mutex_attr);

    return 0;
}

2.2条件变量属性

        POSIX仅为条件变量定义一个属性:pshared。默契为PTHREAD_PROCESS_PRIVATE,当设置为PTHREAD_PROCESS_SHARED时,条件变量能被多个进程中的线程使用。

pthread_condattr_t attr;/*条件变量属性*/

/*初始化条件变量属性为默认值*/

int pthread_condattr_init(pthread_condattr_t *attr);

/*销毁条件变量属性对象*/

int pthread_condattr_destroy(pthread_condattr_t *attr);

/*设置条件变量共享属性*/

int pthread_condattr_setpshard(pthread_condattr_t *attr, int pshared);

psharedPTHREAD_PROCESS_PRIVATEPTHREAD_PROCESS_SHARED

/*获取条件变量共享属性*/

int pthread_condattr_getpshard(pthread_condattr_t *attr, int *pshared);

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

pthread_cond_t cond;

int main(int argc, char *argv[])
{
    int ret;
    pthread_condattr_t cond_attr;

    pthread_condattr_init(&cond_attr);

    ret = pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_PRIVATE);
    if (ret != 0)
    {
        printf("cond setpshared error : %s\n", strerror(ret));
        pthread_exit((void *)0);
    }

    pthread_cond_init(&cond, &cond_attr);

    return 0;
}

2.3线程属性

        POSIX为线程创建定义下列属性:detachstatestacksizestackaddrscop等等。detachstate的默认属性为PTHREAD_CREATE_JOINABLE,即创建被创建为可连接的,意味着由pthread_create创建的该线程ID能被用来与该线程连接并获得它的返回值,或者取消它。当detachstate属性被设置为PTHREAD_CREATE_DETACHED时,即创建的线程ID不能被使用,意味着当线程终止时,它使用的任何资源将立刻被系统回收。

pthread_attr_t attr;/*线程属性*/

/*初始化线程属性为默认值*/

int pthread_attr_init(pthread_attr_t *attr);

/*销毁线程属性对象*/

int pthread_attr_destroy(pthread_attr_t *attr);

/*设置线程分离属性*/

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

psharedPTHREAD_CREATE_DETACHEDPTHREAD_CREATE_JOINABLE

/*获取线程分离属性*/

int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);

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

void *thread_routine(void *arg)
{
    printf("The thread is here\n");
    return NULL;
}

int main(int argc, char *argv[])
{
    int ret;
    pthread_t thread_id;
    pthread_attr_t thread_attr;

    pthread_attr_init(&thread_attr);

    ret = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
    if (ret != 0)
    {
        printf("set detach error : %s\n", strerror(ret));
        pthread_exit((void *)0);
    }

    ret = pthread_create(&thread_id, &thread_attr, thread_routine, NULL);
    if (ret != 0)
    {
        printf("create thread error : %s\n", strerror(ret));
        pthread_exit((void *)0);
    }

    printf("Main exiting\n");

    pthread_exit(NULL);

    return 0;
}

3 取消

当你发现一个线程对完成事情不再是必要时,你可以取消它。除非你确实想要目标线程消失,否则应该尽量避免使用取消操作。

调用pthread_cancel(thread_id)时,它提出取消线程thread_id的请求,线程thread_id在取消请求发出后仍会继续执行,直到到达某个取消点。取消点是线程检查是否被取消并按照请求进行动作的一个位置。

3.1函数

头文件

#include <pthread.h>

函数

int pthread_cancel(pthread_t thread);

参数

thread:线程ID

返回

成功返回0,出错返回错误码

功能

发出取消线程thread的请求


头文件

#include <pthread.h>

函数

int pthread_setcancelstate(int state, int *oldstate);

参数

state:待设置的状态

oldstate:原先的状态

返回

成功返回0,出错返回错误码

功能

设置线程取消状态:启用(PTHREAD_CANCEL_ENABLE)或禁用(PTHREAD_CANCEL_DISABLE


头文件

#include <pthread.h>

函数

int pthread_setcanceltype(int type, int *oldtype);

参数

type:待设置的类型

oldtype:原先的类型

返回

成功返回0,出错返回错误码

功能

设置线程取消类型:推迟(PTHREAD_CANCEL_DELIVEROD)或异步(PTHREAD_CANCEL_ASYNCHRONOUS


头文件

#include <pthread.h>

函数

void pthread_testcancel(void);

参数

void

返回

成功返回0,出错返回错误码

功能

创建一个取消点

3.2取消点

线程取消的方法是向目标线程发CANCELED信号,但如何处理Cancel信号则由目标线程自己决定,或者忽略、或者立即终止、或者继续运行至Cancelation-point(取消点),由不同的Cancelation状态决定。

线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续运行至取消点,也就是说设置一个CANCELED状态,线程继续运行,只有运行至Cancelation-point的时候才会退出。

pthreads标准指定了几个取消点,其中包括:

(1)通过pthread_testcancel调用以编程方式建立线程取消点。
(2)
线程等待pthread_cond_waitpthread_cond_timewait()中的特定条件。
(3)
sigwait(2)阻塞的函数
(4)
一些标准的库调用。通常,这些调用包括线程可基于阻塞的函数。

3.3 实例

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

static int counter;

void *thread_routine(void *arg)
{
    for (counter = 0; ;counter++)
    {
        if ((counter % 1000 ) == 0)
        {
            pthread_testcancel();/*创建取消点*/
        }
    }
}

int main(int argc, char *argv[])
{
    pthread_t thread_id;
    void *result;
    int status;

    status = pthread_create(&thread_id, NULL, thread_routine, NULL);
    if (status != 0)
    {
        printf("create thread error: %s\n", strerror(status));
        pthread_exit((void *)0);
    }
    sleep(2);

    status = pthread_cancel(thread_id);/*发出取消请求*/
    if (status != 0)
    {
        printf("cancel thread error: %s\n", strerror(status));
        pthread_exit((void *)0);
    }

    status = pthread_join(thread_id , &result);
    if (status != 0)
    {
        printf("join thread error: %s\n", strerror(status));
        pthread_exit((void *)0);
    }

    if (result == PTHREAD_CANCELED)
    {
        printf("thrad canceled at iteration %d\n", counter);
    }
    else
    {
        printf("thread was not canceled\n");
    }

    return 0;
}

4 线程私有数据

在进程内的所有线程共享相同的地址空间,即意味着任何声明为静态或外部的变量,或在进程堆声明的变量,都可以被进程内所有线程读写。一个线程真正拥有的唯一私有存储是处理器寄存器,甚至栈地址也能被共享。

程序创建一个键,然后每个线程就能独立地设定或得到自己的键值。键对于所有的线程是相同的,但是每个线程能将它独立的键值与共享键联系。每个线程能在任何时间为键改变它的私有值,而不会影响键或任何另外线程拥有的键值。

4.1函数

pthread_key_t key;/*线程键*/

/*创建线程键*/

int pthread_key_create(pthread_key_t *key, void (*destrutor)(void *));

/*销毁线程键*/

int pthread_key_delete(pthread_key_t key);

/*设置线程私有数据*/

int pthread_setspecific(pthread_key_t key, const void *value);

/*获取线程私有数据*/

void *pthread_getspecific(pthread_key_t key);

4.2实例

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

typedef struct tsd_tag
{
    pthread_t thread_id;
    char *string;
}tsd_t;

pthread_key_t tsd_key;/*线程键*/
pthread_once_t key_once;/*控制变量*/

void once_routine(void)
{
    int ret;
    printf("Initializing key...\n");
    ret = pthread_key_create(&tsd_key, NULL);
    if (ret != 0)
    {
        printf("create key error: %s\n", strerror(ret));
        pthread_exit((void *)0);
    }
}

void *thread_routine(void *arg)
{
    int ret;
    tsd_t *value;

    ret = pthread_once(&key_once, once_routine);
    if (ret != 0)
    {
        printf("pthread once error: %s\n", strerror(ret));
        pthread_exit((void *)0);
    }

    value = (tsd_t *)malloc(sizeof(tsd_t));
    if (value == NULL)
    {
        printf("malloc error");
        pthread_exit((void *)0);        
    }

    /*设置线程私有数据*/
    ret = pthread_setspecific(tsd_key, value);
    if (ret != 0)
    {
        printf("pthread_setspecific error: %s\n", strerror(ret));
        pthread_exit((void *)0);        
    }
    printf("%s set tsd value %p\n", arg, value);
    value->thread_id = pthread_self();
    value->string = (char *)arg;

    /*获取线程私有数据*/
    value = (tsd_t *)pthread_getspecific(tsd_key);
    printf("%s starting ...\n", value->string);

    sleep(2);

    value = (tsd_t *)pthread_getspecific(tsd_key);
    printf("%s done...\n", value->string);

    return NULL;
}

int main(int argc, char *argv[])
{
    int ret;
    pthread_t thread1, thread2;

    ret = pthread_create(&thread1, NULL, thread_routine, "thread 1");
    if (ret != 0)
    {
        printf("create thread 1 error: %s\n", strerror(ret));
        pthread_exit((void *)0);  
    }

    ret = pthread_create(&thread2, NULL, thread_routine, "thread 2");
    if (ret != 0)
    {
        printf("create thread 2 error: %s\n", strerror(ret));
        pthread_exit((void *)0);  
    }

    pthread_exit(NULL);

}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值