12(线程控制)

1 线程属性

int pthread_attr_init(pthread_attr_t *attr);   //初始化线程属性
int pthread_attr_destroy(pthread_attr_t *attr); //释放线程属性空间

线程属性主要有: (1)线程的分离状态属性detachstate, (2)线程栈末尾的警戒缓冲区大小guardsize, (3)线程栈的最低地址statckaddr, (4)线程栈的大小stacksize。 如果对现有某个线程的终止状态不感兴趣的话,可以使用pthread_detach函数让操作系统在线程退出时候收回它所占用的资源。

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

detatchstate取值为: (1)PTHREAD_CREATE_DETACHED 分离状态启动, (2)PTHREAD_CREATE_JOINABLE 正常启动,应用程序可以获取线程的终止状态。

2 同步属性

(1)互斥量属性 有进程共享属性和类型属性两种,进程共享属性是可选的,互斥量属性数据类型为pthread_mutexattr_t。在进程中,多个线程可以访问同一个同步对象,默认情况进程共享互斥量属性为:PTHREAD_PROCESS_PRIVATE。

int pthread_mutexattr_init(pthread_mutexattr_t *attr);  //初始化
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); //回收
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared);  //查询进程共享属性
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,int pshared); //设置进程共享属性
int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr,int *restrict type); //查询类型属性
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); //设置类型属性

(2)读写锁属性: 与互斥量类似,但是只支持进程共享唯一属性,操作函数原型如下:

int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); 
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict attr, int *restrict pshared);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr,int pshared);

(3)条件变量属性: 也是只支持进程共享属性,操作函数原型如下:

int pthread_condattr_destroy(pthread_condattr_t *attr);
int pthread_condattr_init(pthread_condattr_t *attr); 
int pthread_condattr_getpshared(const pthread_condattr_t *restrict attr,int *restrict pshared);
int pthread_condattr_setpshared(pthread_condattr_t *attr,int pshared);

3 线程私有数据

线程模型促进了线程中数据和属性的共享,为什么还要提出合适的用于阻止共享的接口呢?

第一. 有时候需要维护基于每个线程的数据线程ID不能保证是小而连续的整数,即使能保证,也要防止线程间数据的混淆。
第二. 它提供了让基于进程的接口适应多线程环境的机制。

分配线程私有数据过程:首先调用pthread_key_create创建与该数据关联的键,用于获取对线程私有数据的访问权,这个键可以被进程中所有线程访问,但是每个线程把这个键与不同的线程私有数据地址进行关联然后通过调用pthread_setspecific函数把键和线程私有数据关联起来,可以通过pthread_getspecific函数获取线程私有数据的地址。

pthread_key_create函数可以选择为该键关联的析构函数,调用pthread_key_delete函数来取消与线程私有数据值之间的关联关系。通过调用pthread_once函数确保分配的键并不会由于在初始化阶段的竞争而发生变动。

int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)); 
int pthread_key_delete(pthread_key_t key); 
int pthread_once(pthread_once_t *once_control,void (*init_routine)(void));  //避免竞争条件
pthread_once_t once_control = PTHREAD_ONCE_INIT; 
void *pthread_getspecific(pthread_key_t key); //返回线程私有数据值,没有返回NULL
int pthread_setspecific(pthread_key_t key, const void *value);  //设置线程私有数据

4 线程和信号

每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。进程中的信号是传递到单个线程的,进程中的信号屏蔽函数sigprocmask函数在线程中没有定义,线程中必须使用pthread_sigmask。线程可以调用sigwait函数等待一个或者多个信发送。调用pthread_kill函数将信号发送到线程。具体函数原型如下:

#include <signal.h>
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
int sigwait(const sigset_t *set, int *sig);
int pthread_kill(pthread_t thread, int sig);

同步信号案例

//同步信号
#include "apue.h"
#include <pthread.h>
int     quitflag;   /* set nonzero by thread */

sigset_t    mask;

pthread_mutex_t lock    = PTHREAD_MUTEX_INITIALIZER;

pthread_cond_t waitloc  = PTHREAD_COND_INITIALIZER;

void *thr_fn(void *arg)
{
    int err, signo;

    for (;;) 
    {
        err = sigwait(&mask, &signo);//线程等待mask信号集
        if (err != 0)
            err_exit(err, "sigwait failed");

        switch (signo) 
        {
        case SIGINT:
            printf("\ninterrupt\n");
            break;
        case SIGQUIT:
            pthread_mutex_lock(&lock);
            quitflag = 1;
            pthread_mutex_unlock(&lock);
            pthread_cond_signal(&waitloc);//激活pthread_cond_wait(&waitloc, &lock);就是加星号那句语句
            return(0);
        default:
            printf("unexpected signal %d\n", signo);
            exit(1);
        }
    }
}

int main(void)
{
    int     err;
    sigset_t    oldmask;
    pthread_t   tid;

    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGQUIT);//mask信号集中有SIGINT和SIGQUIT

    if ((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0)//阻塞SIGINT和SIGQUIT
        err_exit(err, "SIG_BLOCK error");

    err = pthread_create(&tid, NULL, thr_fn, 0);//建立线程,新线程从thr_fn开始执行

    if (err != 0)
        err_exit(err, "can't create thread");

    pthread_mutex_lock(&lock);

    while (quitflag == 0)
        pthread_cond_wait(&waitloc, &lock);//////*****************
    /*
    必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()的竞争条件。
    mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),
    且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),
    而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。
    在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。
    */

    pthread_mutex_unlock(&lock);

    /* SIGQUIT has been caught and is now blocked; do whatever */
    quitflag = 0;

    /* reset signal mask which unblocks SIGQUIT */
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");
    exit(0);
}

5 线程和fork

子进程从父进程那里继承了所有互斥量、读写锁和条件变量的状态。要清除锁状态,可以通过调用pthread_atfork函数建立fork处理程序

#include <pthread.h>
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
        Returns: 0 if OK, error number on failure

示例

#include "apue.h"
#include <pthread.h>
pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;
void prepare(void)
{
    printf("preparing locks...\n");
    pthread_mutex_lock(&lock1);
    pthread_mutex_lock(&lock2);
}
Void parent(void)
{
    printf("parent unlocking locks...\n");
    pthread_mutex_unlock(&lock1);
    pthread_mutex_unlock(&lock2);
}
Void child(void)
{
    printf("child unlocking locks...\n");
    pthread_mutex_unlock(&lock1);
    pthread_mutex_unlock(&lock2);
}
void *thr_fn(void *arg)
{
    printf("thread started...\n");
    pause();
    return(0);
}
Int main(void)
{
    int         err;
    pid_t       pid;
    pthread_t   tid;

#if defined(BSD) || defined(MACOS)
    printf("pthread_atfork is unsupported\n");
#else
    if ((err = pthread_atfork(prepare, parent, child)) != 0)
        err_exit(err, "can't install fork handlers");
    err = pthread_create(&tid, NULL, thr_fn, 0);
    if (err != 0)
        err_exit(err, "can't create thread");
    sleep(2);
    printf("parent about to fork...\n");
    if ((pid = fork()) < 0)//pthread_atfork在调用fork以后执行
        err_quit("fork failed");
    else if (pid == 0) /* child */
        printf("child returned from fork\n");
    else        /* parent */
        printf("parent returned from fork\n");
#endif
    exit(0);
}

执行结果:

$ ./a.out
thread started...
parent about to fork...
preparing locks...
child unlocking locks...
child returned from fork
parent unlocking locks...
parent returned from fork

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券