linux 线程
来源:互联网 发布:淘宝视频取消自动播放 编辑:程序博客网 时间:2024/05/29 15:04
POSIX线程(P线程)编程
要想知道正在使用的是哪个P线程库:
$ getconf GNU_LIBPTHREAD_VERSION
执行这个命令会显示出LinuxThreads或NPTL及其版本号
进程和线程都有控制流,两者都能同时运行,线程共享数据,进程不共享。
创建线程的时候,线程唯一独有的元素是线程独有的栈,线程的代码和全局变量都是共同的;但进程会复制代码、数据空间、内存栈。
一个GNU/Linux进程可以创建和管理多个线程。线程由线程描述符确定,系统中每个线程的描述符都是唯一的,每个线程都有一个私有的栈和一个独有的上下文环境(程序计数器和存储寄存器等)。线程共享数据空间,他们共享的东西就不只是用户的数据了。例如,打开文件的描述符和套接字是共享的。因此当一个多线程应用程序使用套接字或文件时,要防止多重访问同一资源的情况。
编写多线程应用程序时要注意线程的数据共享问题。
线程函数基础
之前API函数都有一个共同的模式:遇到错误时,返回 -1,错误码保存在进程变量errno中。线程API函数成功时返回0,在遇到错误时,返回一个大于0的错误码。
P线程API:
所有的多线程应用程序都要使pthread函数原型和相应的符号可用。故要包含<pthread.h>
所有的多线程应用程序都必须创建线程,并在最后销毁线程。
Int pthread_create(pthread_t *thread, pthread_attr_t* attr,
Void*(*start_routine)(void*), void*arg);
Int pthread_exit(void* retval);
创建一个新的线程,调用pthread_create,并把pthread_t对象和一个函数(start_routine)结合起来。这个函数表示线程执行的最高层代码。可以由pthread_attr_t(通过pthread_attr_init)提供一系列选项进行属性设置。第四个参数(arg)是在线程创建是时传入的可选参数。
#include<pthread.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<errno.h>
Void * ThreadFunc(void* arg) //线程体函数,运行结束时,自身调用pthread_exit结束
{
printf(“Thread ran\n”);
pthread_exit(arg);
}
Int main()
{
Int ret;
pthread_t mythread;
ret= pthread_create(& mythread, NULL, ThreadFunc, MULL);
//ThreadFunc为线程执行函数
If(ret != 0)
{
Printf(“Can’tcreate pthread(%s)\n”, strerror(errno));
exit(-1);
}
return 0;
}
线程管理
函数pthread_self可以用来取得自己独特的描述符; pthread_t pthread_self();
大多数应用程序需要一些初始化,对于多线程应用程序,初始化很困难,函数pthread_once允许开发人员为一个多线程应用程序创建一个初始化子进程。只会执行一次。
第一个调用pthread_once的线程调用initialize_app,之后调用pthread_once的线程不会再调用initialize_app.
//用pthread_once 提供一个单次使用的初始化函数
#include<pthread.h>
Pthread_once_t my_init_mutex = pthread_once_init;
Void initialize_app(void)
{
//只执行一次的初始化函数
}
Void* myThread(void* arg)
{
Pthread_once(&my_init_mutex, initialize_app);
…
}
线程同步
让线程创建者等待线程结束(也成为“加入”线程),该功能由API函数pthread_join提供。在调用pthread_join时,会挂起调用线程直到加入的线程完成。在加入线程完成后,调用线程会从pthread_join的返回值获得到加入线程的终止状态。
Int phtread_join( pthread_t th, void ** thread_return);
参数th是想要加入的线程,该参数由pthread_create返回或由线程调用pthread_self获取。参数thread_return可以是NULL,表明不捕获线程的返回状态。
//注意: 用默认属性经pthread_create创建的线程都是可以加入的,如果线程的属性设置为detached,则该线程是不可加入的(因为它被设置为与创建线程分离)。
很多情况下,一个线程被建立之后,就不再需要在意它了。那种情况下,可以把它设置为分离的线程,创建者和线程自己都可以完成分离操作,还可以在创建线程时就将其设置为分离的线程(作为属性设置的一部分)。在一个线程被分离之后,它就不能再被加入了。
Int pthread_detach(pthread_t th);
//在线程中调用pthread_detach 把线程分离出去
Void * myThread(void * arg)
{
printf(“Thread %d started\n”, (int)arg);
pthread_detach(pthread_self());
Pthread_exit(arg);
}
///上程序中,线程执行函数中,自己将自己和调用线程分开,该线程退出时,所占用的资源会立即释放(因为这个线程已是分离线程,不会再加入其它线程)。函数pthread_detach成功时返回零,发生错误时返回非零值。
线程互斥
互斥是一个保证线程在关键区正常执行的变量,这些关键区只能由线程独占访问,如果不加保护的话,会导致数据被毁。
要创建一个互斥,只需要声明一个表示互斥的变量,然后用特殊符号常量初始化。
pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER;
互斥初始化可以有不同的特殊符号常量:
PTHREAD_MUTEX_INITIALIZER 快速互斥
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP 递归互斥
PTHREAD_ERROCHECK_MUTEX_INITIALIZER_NP 检查错误的互斥
有了一个互斥,可以锁定或解锁它,从而创建关键区。通过pthread_mutex_lock和pthread_mutex_unlock来实现锁定和解锁。函数pthread_mutex_trylock用于尝试锁定互斥,但如果互斥已经锁定,它不会阻断。可以用pthread_mutex_destroy来销毁已经存在的互斥。
Int thread_mutex_lock( pthread_mutex_t *mutex);
Int thread_mutex_unlock ( pthread_mutex_t *mutex);
Int thread_mutex_trylock( pthread_mutex_t *mutex);
Int thread_mutex_destroy( pthread_mutex_t *mutex);
锁定一个线程意味着进入了一个关键区,在互斥锁定后,可以安全的进入关键区而不必担心数据毁损或多重访问,要退出关键区,需要解锁互斥,然后退出。
Pthread_mutex_t cntr_mutex = PTHREAD_MUTEX_INITIALIZER;
……
assert(pthread_mutex_lock(&cntr_mutex)== 0);
//关键区
//增加保护计数
Counter ++;
assert(pthread_mutex_unlock(&cntr_mutex) == 0);
pthread_mutex_trylock 操作的意义在于如不能锁定互斥,或许可以做点别的事情而不是阻断在pthread_mutex_lock调用上:
ret =pthread_mutex_trylock(&cntr_mutex);
if (ret == EBUSY)
{
//不能锁定,做其他的事情
}
Else if (ret == EINVAL)
{
//关键区错误
Assert(0);
}
Else
{
//关键区操作
ret =thread_mutex_unlock(&cntr_mutex);
}
要销毁线程,调用pthread_mutex_destroy函数,函数pthread_mutex_destroy仅在互斥当前没有被任何线程锁定时才能成功执行,如果互斥正被锁定,函数会失败并返回错误码:EBUSY,
Ret =pthread_mutex_destroy(&cntr_mutex);
If (ret == EBUSY)
{
//互斥被锁定,无法销毁
}
Else
{
//互斥被销毁
}
线程条件变量
条件变量是一个特殊的线程结构体,允许一个线程基于条件唤醒另一个线程。条件变量允许一个线程等待某个事件,让另一个线程在事件发生时向它发出信号。
P线程API提供了很多支持条件变量的函数,这些函数提供条件变量的创建、等待、信号和销毁功能。
Int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
Int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
Conststruct timespec *abstime);
Int pthread_cond_signal(pthread_cond_t *cond);
Int pthread_cond_broadcast(pthread_cond_t *cond);
Int pthread_cond_destroy(pthread_cond_t *cond);
要创建一个条件变量,只需要创建一个pthread_cond_t类型的变量,把它设置为PTHREAD_COND_INITIALIZER就可以完成初始化(和互斥的创建于初始化相似)。
Pthread_cond_t recoveryCond = PTHREAD_COND_INITIALIZER;
条件变量要求互斥的存在,并会和互斥联合工作。
(创建互斥:pthread_mutex_t recoveryMutex= PTHREAD_MUTEX_INITIALIZER)
向一个线程发送信号:
1、 互斥先锁定
2、 调用signal函数
3、 完成后 再解锁互斥
向一个线程发送信号需要调用 pthread_cond_signal函数:
pthread_mutex_lock(&recoveryMutex);
pthread_cond_signal(&recoveryCond);
pthread_mutex_unlock(&recoveryMutex);
解锁互斥后,指定的一个线程收到信号并继续执行。在互斥解锁后,一系列线程都会恢复运行(不过他们也依赖于互斥,所以实际上是一个接一个的恢复运行)
P 线程API支持把时间等待作为条件等待的一种,函数pthread_cond_timewait,允许调用者设定一个绝对时间,用于规定什么时候放弃任务返回调用者
Struct timeval currentTime;
Struct timespec expireTime;
Int ret;
….
Assert(pthread_mutex_lock(&recoveryMutex)== 0)
gettimeofday(¤tTime);
expireTime.tv_sec = currentTime.tv_sec + 1;
expireTime.tv_nsec =currentTime.tv_usec*1000;
ret = 0;
while((workload < MAX_NORMAL_WORKLOAD)&&(ret !=ETIMEOUT))
{
ret= pthread_cond_timewait(&recoveryCond, &recoveryMutex,&expireTime);
}
If (ret == ETIMEOUT)
{
//
}
Else
{
//条件到达时,执行
}
assert(pthread_mutex_unlock(&recoveryMutex)== 0);
- linux 线程 线程属性
- linux 线程 线程同步
- linux 线程
- linux线程
- Linux 线程
- linux 线程
- linux线程
- Linux “线程”
- Linux线程
- linux 线程
- linux 线程
- linux线程
- linux 线程
- linux--线程
- Linux线程
- linux 线程
- linux 线程
- linux线程
- adds events to the mouse
- Extjs 实现文件上传
- sqlserver 修改字段类型,同表查询重复记录sql语句
- linux中fork()函数详解
- Android的触摸(Touch)机制
- linux 线程
- java定时器,在web工程中执行
- ScrollView中添加一个android:fillViewport="true"
- std string的内存共享和Copy-On-Write技术
- hdu 4044 GeoDefense (树形dp | 多叉树转二叉树)
- Lambda 表达式
- centos vsftp
- OFBIZ研究心得之三
- jar打包 jar line too long 异常处理方法