pthread 学习
1. 创建线程
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:分离的状态,和主线程分离,自己运行结束后并释放资源;
2.__schedpolicy(调度策略):
SCHED_OTHER:正常、非实时,默认状态
SCHED_RR:实时、轮转法
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 = ; const char* data = "tread1";
pthread_setspecific(key, data); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, );
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, ); pthread_cleanup_push(pthread_mutex_unlock, &mutex); while () {
printf("thread 1 wait %s \n", pthread_getspecific(key)); //pthread_cond_wait(&cond, &mutex);
int state = pthread_mutex_trylock(&mutex);
if (state == ) { 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(); return ;
} void* thread_2_func (void* arg) {
static int i = ; const char* data = "tread2";
pthread_setspecific(key, data); while () {
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 == ) {
pthread_cancel(thread_1);
}
if (i == ) {
pthread_mutex_unlock(&mutex);
pthread_exit();
}
i++; printf("thread 2 end %d \n", i); pthread_mutex_unlock(&mutex);
} return ;
} void* thread_3_func (void* arg) {
static int i = ; while () {
printf("thread 3 start \n"); sleep(); pthread_cond_signal(&cond); if (i == )
break;
i++; printf("thread 3 end \n");
} return ;
} void destroy (void* arg) {
printf("pthread destroy key \n");
} int main(int argc, const char * argv[]) {
pthread_cond_init(&cond, );
pthread_mutex_init(&mutex, );
pthread_key_create(&key, destroy); if (pthread_create(&thread_1, , thread_1_func, )) {
printf("thread 1 create failed... \n");
}
if (pthread_create(&thread_2, , thread_2_func, )) {
printf("thread 2 create failed... \n");
}
if (pthread_create(&thread_3, , thread_3_func, )) {
printf("thread 3 create failed... \n");
} //sleep(5); pthread_join(thread_1, );
pthread_join(thread_2, );
pthread_join(thread_3, ); pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
pthread_key_delete(key); printf("exit \n"); return ;
}
pthread 学习的更多相关文章
- linux和android端的pthread学习
本文起初主要想写个演示样例实測下pthread_mutex_lock和pthread_mutex_trylock差别.在linux机器上非常快就over了,可是想了一下.pthread是unix系的, ...
- pthread 学习系列 case2-- 使用互斥锁
ref http://www.ibm.com/developerworks/cn/linux/thread/posix_thread1/index.html #include <pthread. ...
- pthread 学习系列 case1-- 共享进程数据 VS 进程
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <pthread.h& ...
- PThread 学习笔记
POSIX 线程,也被称为Pthreads,是一个线程的POSIX标准: pthread.h int pthread_create(pthread_t * thread, pthread_attr_t ...
- pthread 学习系列 case2-- pthread_mutex_t
许多互斥对象 如果放置了过多的互斥对象,代码就没有什么并发性可言,运行起来也比单线程解决方案慢.如果放置了过少的互斥对象,代码将出现奇怪和令人尴尬的错误.幸运的是,有一个中间立场.首先,互斥对象是用于 ...
- pthread多线程编程的学习小结
pthread多线程编程的学习小结 pthread 同步3种方法: 1 mutex 2 条件变量 3 读写锁:支持多个线程同时读,或者一个线程写 程序员必上的开发者服务平台 —— DevSt ...
- clone的fork与pthread_create创建线程有何不同&pthread多线程编程的学习小结(转)
进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合,这些资源在Linux中被抽 象成各种数据对象:进程控制块.虚存空间.文件系统,文件I/O.信号处理函数.所以创建一个进程的 过程就是这 ...
- linux pthread之学习篇
在应用程序编程中,为了不影响与用户交互的性能,通常需要创建新的线程来处理一些比较耗时的. 不影响用户体验的工作.而这又通常分为两种情况: (1)需要临时创建一个线程来做某件特定的事,等事情做完时线程即 ...
- Linux学习笔记(15)-信号量
在多线程或者多进程编程中,有一个非常需要关注的东西,那就是同步以及互斥问题. 同步是指多个进程之间的协作,而互斥是指多个进程之间,为了争夺有限的资源,而进行的竞争. 理论很高端,但经过自己几天的学习, ...
随机推荐
- get------引用接口
关于引用接口 1. 通过get方式 2. String poiUrl="http://接口地址?接口ID=接口给你的ID&参数1=?&参数2=?&参数 ...
- linux 模块常用命令
lsmod | grep pcspkr 查看pcspkr模块是否运行modprobe -r pcspkr 删除pcspkr模块modinfo pcspkr 查看pcspkr模块信息m ...
- Ajax在html页面获取后台XML文件资源
一.准备工具 站长吧ASP调试工具.exe,这个工具是为了快速建立asp环境,方便调试. 二.建立文件夹 1.建立网站根文件夹,名字随意,将站长吧ASP调试工具.exe复制到根文件夹: 2.建立xml ...
- pwnable echo2
pwnable echo2 linux 32位程序 涉及FSB和UAF漏洞的利用,先%x泄露地址,然后利用UAF漏洞进行利用 code:
- Web前端MVC框架
MVC: 模型层(model).视图层(view).控制层(controller) Model:即数据模型,用来包装和应用程序的业务逻辑相关的数据或者对数据进行处理,模型可以直接访问数据. View: ...
- struts2中web.xml的配置
在web.xml</web-app>前添加的<filter></filter>和<filter-mapping></filter-mapping& ...
- Linux LVM硬盘管理之一:概念介绍
一.LVM概念介绍: LVM是 Logical Volume Manager(逻辑卷管理)的简写,它由Heinz Mauelshagen在Linux 2.4内核上实现.LVM将一个或多个硬盘的分区在逻 ...
- XSS的原理分析与解剖
0×01 前言: <xss攻击手法>一开始在互联网上资料并不多(都是现成的代码,没有从基础的开始),直到刺的<白帽子讲WEB安全>和cn4rry的<XSS跨站脚本攻击剖析 ...
- mvc中Url.RouteUrl或者Html.RouteLink实现灵活超链接,使href的值随路由名称或配置的改变而改变[bubuko.com]
mvc,超链接除了直接写在a标签的href内还可以使用路由规则来生成,这样在改变了路由规则或者路由名称时不用再去代码中更改href的值,而且还容易遗漏.借助Url.RouteUrl或者Html.Rou ...
- Rails 4.0 移除了 XML 参数解析器。若要使用请加入 actionpack-xml_parser
拜读了用 Rails 搭建微信公众平台 API之后发现, params[:xml]这个办法在Rails 4里面已经被办掉了,于是就看了一下Rails 4的新特性发现XML Parameter pars ...