POSIX线程(pthread)库
POSIX线程库是用于C/C++的基于标准的线程API。它允许产生一个新的并发流程。它在多处理器或多核系统上最为有效,在这些系统中,可以将流程安排在另一个处理器上运行,从而通过并行或分布式处理提高速度。线程比“forking”或生成新进程所需的开销更少,因为系统不会为进程初始化新的系统虚拟内存空间和环境。虽然在多处理器系统上最有效,但在单处理器系统上也可以找到增益。
使用线程在图形界面程序尤为有效,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。
pthread库需要头文件
pthread.h
编译链接参数-lpthread
创建线程
数据类型
pthread_t 线程ID
pthread_attr_t 线程属性
创建线程函数
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
参数说明
thread:返回线程ID, (在bits/pthreadtypes.h文件中定义为unsigned long int类型)
attr:线程属性,如果要使用默认属性直接传递 NULL
start_routine:线程函数,它是一个函数指针类型,返回类型为 void *,参数为一个 void * 类型变量,创建好这样类型的一个函数,将函数名传递进去即可。
arg:线程函数参数的指针,如果有多个参数,可以传递一个指向参数结构体的指针
返回值说明
成功情况下返回 0,失败情况下返回错误码
获取线程ID
pthread_t pthread_self(void);
该函数返回调用它的线程的线程ID
通过线程ID比较线程是否相等
int pthread_equal(pthread_t t1, pthread_t t2);
如果两个线程相等,返回非0值,如果不相等,返回0
分离线程
int pthread_detach(pthread_t thread);
将线程 ID 为 thread 的线程分离出去,所谓分离出去就是指主线程不需要再通过 pthread_join 等方式等待该线程的结束并回收其线程控制块(TCB)的资源,被分离的线程结束后由操作系统负责其资源的回收。
成功情况下返回 0,失败情况下返回错误码。
额外说明
一般来说,主线程是要负责创建出来的子线程的资源回收工作的。如果主线程先于子线程退出并且子线程没有设置为分离状态,那么子线程结束后其资源是无法得到回收的,会造成资源浪费和系统臃肿;如果主线程先于子线程退出但是子线程时分离状态,那么子线程退出的时候操作系统会自动回收其资源。
终止线程
终止线程的三种方式
a. 线程从启动例程返回,返回值就是线程的退出码
b. 线程可以被同一进程中的其他线程取消
取消线程
int pthread_cancel(pthread_t thread);
向线程发送取消请求,成功返回0,失败返回错误码
c. 线程自身调用pthread_ exit()函数
终止线程
void pthread_exit(void *retval);
参数
retval 返回值
pthread_exit()不返回任何值,如果线程未分离,则可以使用pthread_join()从另一个线程检查线程id和返回值。
注意:指针*retval指向的内容不能为函数中局部变量,因为一旦线程函数终止,它们将不再存在。
注意:如果在主线程中调用了 pthread_exit(NULL),则主线程退出,如果子线程存在会继续执行
等待线程结束
int pthread_join(pthread_t th, void **thread_return);
参数:
th 阻塞当前线程,直到th指定的线程终止。线程终止可以通过调用pthread_exit()函数或者被取消
thread_return 如果线程返回不为NULL,返回值将被储存在thread_return
可以在主线程中使用pthread_join()等待子线程执行结束后再退出,否则主线程退出后,未执行完的子线程也会随之结束。
测试线程的使用
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
void* func_a(void *pointer){
int i = 0;
for(i = 0; i < 3; i++){
sleep(1);
printf("thread %d running\n",*(int*)&pointer);
}
}
int main(int argc, char* argv[]){
pthread_t threadID[5];
int i = 0;
//创建线程
for(i = 0; i < 5; i++){
pthread_create(&threadID[i],NULL,func_a,(void *)i);
}
//等待所有线程结束后再退出主线程
for(i = 0; i < 5; i++){
pthread_join(threadID[i],(void**)0);
}
return 0;
}
线程同步
pthread线程库提供三种同步机制:
互斥锁:阻止其他线程访问变量。这强制线程对变量或变量集进行独占访问。
pthread_join() 使线程等待其他线程结束。
条件变量-数据类型 pthread_cond_t
使用线程锁进行数据同步的办法:
线程锁函数
pthread_mutex_t lock; /* 互斥锁定义 */
pthread_mutex_init(&lock, NULL); /* 动态初始化, 成功返回0,失败返回非0 */
pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; /* 静态初始化 */
pthread_mutex_lock(&lock); /* 阻塞的锁定互斥锁 */
pthread_mutex_trylock(&thread_mutex);/* 非阻塞的锁定互斥锁,成功获得互斥锁返回0,如果未能获得互斥锁,立即返回一个错误码 */
pthread_mutex_unlock(&lock); /* 解锁互斥锁 */
pthread_mutex_destroy(&lock) /* 销毁互斥锁 */
测试线程锁的代码
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
//定义互斥锁
pthread_mutex_t g_mutex;
int g_ia = 0;
void *func_c(void *pointer){
while(1){
//加锁
//pthread_mutex_lock(&g_mutex);
g_ia++;
//解锁
//pthread_mutex_unlock(&g_mutex);
sleep(1);
}
pthread_exit((void**)0);
}
int main(int argc, char* argv[]){
pthread_t threadID;
pthread_create(&threadID,NULL,func_c,NULL);
//初始化锁
pthread_mutex_init(&g_mutex,NULL);
int i = 0;
for(i = 0; i<5; i++){
//加锁
//pthread_mutex_lock(&g_mutex);
fprintf(stderr,"%d ",g_ia);
sleep(1);
fprintf(stderr,"%d ",g_ia);
sleep(1);
fprintf(stderr,"%d \n",g_ia);
//解锁
//pthread_mutex_unlock(&g_mutex);
sleep(1);
}
pthread_cancel(threadID);
return 0;
}
不加锁,在一个循环内读取频繁的被线程修改
将加锁的语句注释取消,对数据加锁保护,在每个while循环内数据没有被线程修改