pthread 学习

1. 建立线程api

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

  thread:线程句柄,用于标示一个线程;安全

  attr(线程属性结构):函数

  1.__detachstate(同步状态):性能

    PTHREAD_CREATE_JOINABLE:结合的状态,在主线程退出以前,应该等待子线程完成;测试

    PTHREAD_CREATE_DETACH:分离的状态,和主线程分离,本身运行结束后并释放资源;spa

  2.__schedpolicy(调度策略):线程

    SCHED_OTHER:正常、非实时,默认状态unix

    SCHED_RR:实时、轮转法code

    SCHED_FIFO:实时、先入先出

  3.__schedparam:目前只有一个sched_priority(调度优先级)属性

  4.__inheritsched:

    PTHREAD_EXPLICIT_SCHED:显式指定调度策略和调度参数;

    PTHREAD_INHERIT_SCHED:继承调用线程的参数;

  5.__scope:

    PTHREAD_SCOPE_SYSTEM:与系统中全部线程一块儿竞争CPU时间,默认值;

    PTHREAD_SCOPE_PROCESS:与同进程中的线程竞争CPU;

  start_routine:线程启动函数;

  arg: 线程启动的时候,传递给启动函数的参数,若是没有就传入0;

设置属性的一些api:

  pthread_attr_init(pthread_attr_t*)

  pthread_attr_destroy(pthread_attr_t*)

  pthread_attr_set-/get- 一系列函数

 

2. 线程的取消

一个线程能够向另外一个线程发送取消请求,来终止另外一个线程的执行,不过另外一个线程能够设置是否忽略取消请求,若是在不忽略的状况下,根据线程的设置,多是执行到下一个取消点(Cancelation-point)才被终止执行,也多是当即终止执行。

线程的取消点:

  pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait()、sigwait()等函数以及read()、write()会引发系统阻塞的函数都是取消点。

  对于没有取消点的执行线程,咱们能够在须要被取消执行的地方调用pthread_testcancel(),当一个线程接受到取消请求并无忽略的状况下,一旦执行到pthread_testcancel()就会终止执行。

API:

int pthread_cancel(pthread_t); 向一个线程发送取消请求,成功返回0,不然返回非0,成功发送不表明已经完成取消;

int pthread_setcancelstate(int state, int *oldstate); 设置取消状态,有PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE两种,第一种是接受取消请求,第二种是忽略取消请求。oldstate若是不为0,用于存入以前的取消状态,以便未来能够恢复。

int pthread_setcanceltype(int type, int *oldtype); 设置取消动做的时机,有PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS两种,第一种是执行到下一个取消点终止,第二种是当即终止执行。oldtype若是不为0,用于存入以前的值,以便未来能够恢复。

注:在unix环境下测试,两种状况都是运行到下一个取消点或者有pthread_testcancel()的位置才终止,不清楚具体缘由。

 

3. 互斥锁

  因为线程具备抢占式和共享资源的特色,所以须要用一种互斥机制来保证线程之间的同步,最经常使用的就是互斥锁。

建立互斥锁:

  静态方式:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  动态方式:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); mutexattr缺省值为0;

销毁互斥锁:

  int pthread_mutex_destroy(pthread_mutex_t *mutex); 销毁锁占用的资源,若是锁定状态则返回EBUSY;

锁类型,在pthread_mutexattr_t(属性)中指定:

  PTHREAD_MUTEX_TIMED_NP:普通锁(缺省值)。当一个线程加锁之后,其他请求锁的线程将造成一个等待队列,并在解锁后按优先级得到锁;

  PTHREAD_MUTEX_RECURSIVE_NP:嵌套锁。容许同一个线程对同一个锁成功得到屡次,并经过屡次unlock解锁。若是是不一样线程请求,则在加锁线程解锁时从新竞争;

  PTHREAD_MUTEX_ERRORCHECK_NP:检错锁。若是同一个线程请求同一个锁,则返回EDEADLK,不然与PTHREAD_MUTEX_TIMED_NP类型动做相同。这样就保证当不容许屡次加锁时不会出现最简单状况下的死锁;

  PTHREAD_MUTEX_ADAPTIVE_NP:适应锁。动做最简单的锁类型,仅等待解锁后从新竞争;

锁操做:

  不管是什么类型的锁,都只能被一个线程获取,不可能出现被两个线程同时获取到锁的状况。

  普通锁和适应锁,解锁者能够是任何线程;

  检错锁则必须由加锁者解锁才有效,不然返回EPERM;

  嵌套锁,文档和实现要求必须由加锁者解锁;

  int pthread_mutex_lock(pthread_mutex_t *mutex); 请求上锁,若是没有获取到锁,线程会被阻塞;

  int pthread_mutex_unlock(pthread_mutex_t *mutex); 解锁;

  int pthread_mutex_trylock(pthread_mutex_t *mutex); 请求上锁,若是成功得到一个锁,返回0,不然返回非0,该函数当即返回,不会阻塞;

锁机制并非线程安全的,若是在线程上锁以后,解锁以前,被取消,就可能出现死锁的状况。这种状况须要使用线程退出回调函数来解锁。 

 

4. 条件变量

  线程上锁以后,应该尽量快的解锁,好让其余线程能够快速获取锁,以便提升程序性能。但不管如何,相关线程都会频繁的循环检测锁状态以便获取锁,例如可能在某种状况下,某个线程会在大部分时间上锁失败,这样不只消耗了cpu周期,并且也是无心义的检测。所以,条件变量很好的改善了这种状况。

  条件变量是一种线程间的同步机制。主要利用了一个共享的条件变量来进行同步,一个线程等待条件变量进行挂起,而一个线程在条件成立的时候,唤醒阻塞的线程。为了防止竞争和对资源的保护,条件变量老是和互斥锁一块儿使用。

建立条件变量:

  静态方式:pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

  动态方式:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);

  int pthread_cond_destroy(pthread_cond_t *cond);

    销毁条件变量,若是条件变量在被等待,返回EBUSY;

使用条件变量:

  int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

    首先对mutex解锁(所以在调用pthread_cond_wait前,mutex必须处于锁定状态),而后等待cond的唤醒,此时该函数并不会当即返回而是被阻塞,直到cond被唤醒,而后对mutex上锁,该函数返回。

  int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);

    和pthread_cond_wait相似,只是最有一个参数代表在规定时刻前条件没有成立,返回ETIMEOUT,并结束等待。

  int pthread_cond_signal(pthread_cond_t*); 

    唤醒队列中第一个线程;

  int pthread_cond_broadcast();

    唤醒全部等待条件变量的线程;

因为wait操做也是一个取消点,所以在wait操做结束以后,若是线程执行了取消操做,那么mutex会被一直锁定,形成死锁。这情状况须要使用退出回调函数来解锁。

  

5. 终止线程

  正常终止:函数调用return和执行pthread_exit(pthread_t),这种是代码能够控制的终止状况;

  异常终止:被其余线程取消,或者线程执行发生异常;

  异常终止可能会致使资源不能被正常释放,其中包括锁,若是锁资源被解锁前,线程就异常终止了,那么其余线程将永远没法再得到该锁。

线程终止的清理:

  使用pthread提供的清理函数(因为清理函数是宏定义,必须在同一层做用域成对出现,才能编译经过):

  void pthread_cleanup_push(void (*routine) (void *), void *arg);

    入栈一个清理函数和须要清理的对象;

  void pthread_cleanup_pop(int execute);

    在线程退出的时候,执行栈中的清理函数;

  清理函数会在自动释放资源,出如今函数对中的代码段的任何终止动做,都将执行清理函数。

  若是须要在线程终止时,自动释放锁资源,可使用:

    pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);

    pthread_mutex_lock(&mut);

    ......

    pthread_mutex_unlock(&mut);

    pthread_cleanup_pop(0);

线程的终止:

  void pthread_exit(void *retval);

    线程终止本身的执行,并清理资源。

  int pthread_join(pthread_t th, void **thread_return);

    合并线程,若是是joinable的子线程,一般在主线程结束以前,必须调用该函数等待子线程的执行完成,然和合并到主线程。thread_return若是不为0,保存线程的返回值。

  int pthread_detach(pthread_t th);

    分离线程,若是是detachable的子线程,它会在本身执行完成后,自动清理资源,主线程不须要等待它完成;

 

5. 线程私有数据(TSD)

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

  该函数从TSD池中分配一项,将其值赋给key供之后访问使用。若是destr_function不为空,在线程退出(pthread_exit())时将以key所关联的数据为参数调用destr_function(),以释放分配的缓冲区。

  不论哪一个线程调用pthread_key_create(),所建立的key都是全部线程可访问的,但各个线程可根据本身的须要往key中填入不一样的值,这就至关于提供了一个同名而不一样值的全局变量。

  TSD池用一个结构数组表示:

    static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] = { { 0, NULL } };

  建立一个TSD就至关于将结构数组中的某一项设置为"in_use",并将其索引返回给*key,而后设置destructor函数为destr_function。

  int pthread_key_delete(pthread_key_t key);

  这个函数并不检查当前是否有线程正使用该TSD,也不会调用清理函数(destr_function),而只是将TSD释放以供下一次调用pthread_key_create()使用。在LinuxThreads中,它还会将与之相关的线程数据项设为NULL(见"访问")。

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

  void * pthread_getspecific(pthread_key_t key);

  写入(pthread_setspecific())时,将pointer的值(不是所指的内容)与key相关联,而相应的读出函数则将与key相关联的数据读出来。数据类型都设为void *,所以能够指向任何类型的数据。

 

实例:

//
//  main.c
//  threads
//
//  Created by avl-showell on 16/7/13.
//  Copyright © 2016年 avl-showell. All rights reserved.
//

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

pthread_t thread_1, thread_2, thread_3;
pthread_cond_t cond;
pthread_mutex_t mutex;
pthread_key_t key;

void* thread_1_func (void* arg) {
    static int i = 0;
    
    const char* data = "tread1";
    pthread_setspecific(key, data);
    
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, 0);
    
    pthread_cleanup_push(pthread_mutex_unlock, &mutex);
    
    while (1) {
        printf("thread 1 wait %s \n", pthread_getspecific(key));
        
        //pthread_cond_wait(&cond, &mutex);
        int state = pthread_mutex_trylock(&mutex);
            if (state == 0) {
            
            printf("thread 1 start \n");
            
            //sleep(1);
            
            pthread_testcancel();
            
            i++;
            
            printf("thread 1 end %d \n", i);
            
            pthread_mutex_unlock(&mutex);
        }
        else {
            printf("thread 1 get lock failed... \n");
        }
    }
    
    pthread_cleanup_pop(0);
    
    return 0;
}

void* thread_2_func (void* arg) {
    static int i = 0;
    
    const char* data = "tread2";
    pthread_setspecific(key, data);
    
    while (1) {
        pthread_mutex_lock(&mutex);
        
        printf("thread 2 wait %s \n", pthread_getspecific(key));
        
        pthread_cond_wait(&cond, &mutex);
        
        printf("thread 2 start \n");
        
        //sleep(1);
        
        if (i == 0) {
            pthread_cancel(thread_1);
        }
        if (i == 8) {
            pthread_mutex_unlock(&mutex);
            pthread_exit(0);
        }
        i++;
        
        printf("thread 2 end %d \n", i);
        
        pthread_mutex_unlock(&mutex);
    }
    
    return 0;
}

void* thread_3_func (void* arg) {
    static int i = 1;
    
    while (1) {
        printf("thread 3 start \n");
        
        sleep(1);
        
        pthread_cond_signal(&cond);
        
        if (i == 20)
            break;
        i++;
        
        printf("thread 3 end \n");
    }
    
    return 0;
}

void destroy (void* arg) {
    printf("pthread destroy key \n");
}

int main(int argc, const char * argv[]) {
    pthread_cond_init(&cond, 0);
    pthread_mutex_init(&mutex, 0);
    pthread_key_create(&key, destroy);
    
    if (pthread_create(&thread_1, 0, thread_1_func, 0)) {
        printf("thread 1 create failed... \n");
    }
    if (pthread_create(&thread_2, 0, thread_2_func, 0)) {
        printf("thread 2 create failed... \n");
    }
    if (pthread_create(&thread_3, 0, thread_3_func, 0)) {
        printf("thread 3 create failed... \n");
    }
    
    //sleep(5);

    pthread_join(thread_1, 0);
    pthread_join(thread_2, 0);
    pthread_join(thread_3, 0);
    
    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&mutex);
    pthread_key_delete(key);
    
    printf("exit \n");
    
    return 0;
}
相关文章
相关标签/搜索