线程:可以理解为轻量级的CPU执行单元。其必须依托在进程内部。
同一个进程里面的多个线程是共享进程的资源的。但是线程也有自己的私有数据,包括:
线程号(thread ID)寄存器堆栈信号掩码优先级线程私有存储空间线程的头文件为pthread.h 链接库为libpthread.a
线程常用函数
函数说明pthread_t pthread_self(void)获取本线程的线程IDint pthread_equal(pthread_t thread1,pthread_t thread2)判断两个线程ID是否指向同一个线程int pthread_once(pthread_once_t *once_control,void(*int_routine)(void))用来保证init_routine线程函数在进程中仅执行一次测试程序
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> int* thread(void *arg) { pthread_t newthid; newthid = pthread_self(); printf("this is a new thread ,thread ID =%u\n",newthid); return NULL; } int main(void) { pthread_t thid; printf("main thread ,ID is %u\n",pthread_self());//打印主线程的ID if(pthread_create(&thid,NULL, (void *)thread,NULL)!=0) { printf("thread creation failed \n"); exit(1); } sleep(1); exit(0); }gcc在编译的时候,不指定库,会报错 原因 由于pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,在编译中要加-lpthread参数: gcc -o pthread -lpthread pthread.c
一个有趣的现象 我在程序中死循环创建线程,且线程不退出,发现,CPU的使用率确实很高,但是整个系统并没有卡死的现象。
参数pthread_attr_t结构体的具体含义 datachstate:表示创建的线程是否与进程中的其他线程脱离同步。 schedpolicy:新线程调度策略 schedparam:调度涉及到的一个结构体(sched_param) inheritsched:调度是否用继承的。 scop:线程间的CPU竞争范围 guardsize:警戒堆栈大小 stackaddr_set:堆栈地址集 stacksize:堆栈的大小
使用pthread_exit() 有两种情况会导致线程退出:1.main函数return或exit了。2.主线程调用了pthread_exit。 线程终止,需要关注两个大问题: 1.临界资源(就是资源在一个时间内只有一个线程能用,想用的线程只能等待。)。在终止前,需要做好释放工作。具体如下: linux系统提供了一对函数:pthread_cleanup_push()和pthread_cleanup_pop()用于自动释放资源。在使用时,这两个函数必须成对使用,且在同一段代码内部。(原因是这两个函数的本质其实是一段宏代码)。 2.线程同步问题。线程中也有类似进程的wait函数,其作用就是等待某一个线程结束了自己才结束。对应的函数是pthread_join()。需要注意
被等待的线程不能处于DETACHED状态(既调用pthread_detach函数)。一个线程不能被多个线程等待。否则出了第一个会正常收到返回,其他都会返回错误代码ESRCH。示例代码:
#include <stdio.h> #include <pthread.h> void assisthread(void *arg) { printf("I am helping to do some jobs\n"); sleep(3); pthread_exit(0); } int main(void) { pthread_t assistthid; int status; pthread_create(&assistthid,NULL ,(void *)assisthread,NULL); pthread_join(assistthid,(void*)&status); printf("assistthread's exit is cause %d\n",status); return 0; }运行结果:
gcc -o jointhread jointhread.c -lpthread #编译 svauto@ubuntua:~/C/thread$ ./jointhread #运行 I am helping to do some jobs assistthread's exit is cause 0程序可以看出主线程在执行pthread_join后处于等待状态,知道子线程退出后,才退出主线程。
在多线程中,进程的数据空间是共享的,有时候线程需要有自己的私有全局数据,应该怎么做呢? 线程私有数据使用:“一键多值”技术。创建“键”的时候,Linux从TSD池中分配一项,当一个键被创建后,所有的线程都可以访问,但是不同线程从这个键中取到的值是不一样的。这就是一键多值的含义。 涉及到的函数有四个
函数作用pthread_key_create创建一个键pthread_setspecific为一个键设置线程私有数据pthread_getspecific从一个键获取私有数据pthread_key_delete删除一个键实例代码:
#include <stdio.h> #include <string.h> #include <pthread.h> pthread_key_t key; void * thread2(void *arg) { int tsd=5; printf("thread %x is running \n",pthread_self()); pthread_setspecific(key,(void *)tsd); printf("thread %x returns %d \n",pthread_self(),pthread_getspecific(key)); } void *thread1(void *arg) { int tsd = 0; pthread_t thid2; printf("thread %x is running \n",pthread_self()); pthread_setspecific(key,(void *)tsd); pthread_create(&thid2,NULL,thread2,NULL); sleep(2); printf("thread %x returns %d\n",pthread_self(), pthread_getspecific(key)); } int main(void) { pthread_t thid1; printf("main thread begins running\n"); pthread_key_create(&key,NULL); pthread_create(&thid1,NULL,thread1,NULL); sleep(3); pthread_key_delete(key); printf("main thread exit\n"); return 0; }运行结果: 可以看出两个线程的私有数据,是不会互相影响的。
linux有3种方式处理线程的同步:
互斥锁条件变量异步信号1.互斥锁 在同一时刻,通常只允许一个线程执行一个关键部分的代码。涉及到的函数如下
函数功能pthread_mutex_init初始化一个互斥锁pthread_mutex_destroy注销一个互斥锁pthread_mutex_lock加锁,如果不成功则阻塞等待pthread_mutex_unlock解锁pthread_mutex_trylock测试加锁,如果不成功则立即返回,错误码为EBUSY互斥锁初始化 初始化方法有两种 实例代码 下面代码用互斥锁的方式,保护全局变量在各线程之间同步(注意:此时不能在代码中直接操作全局变量,而应该调用下面的read ,write函数)
#include <stdio.h> #include <string.h> #include <pthread.h> pthread_mutex_t number_mutex; int globalnumber; void write_globalnumber() { pthread_mutex_lock(&number_mutex); globalnumber++; printf("now globalnumber=%d\n",globalnumber); pthread_mutex_unlock(&number_mutex); } int read_globalnumber() { int temp; pthread_mutex_lock(&number_mutex); temp= globalnumber; pthread_mutex_unlock(&number_mutex); return (temp); } void * thread2(void *arg) { int tsd=5,i=20; printf("thread2 %x is running \n",pthread_self()); while(i>0) { printf("read =%d\n",read_globalnumber()); i--; usleep(1); } } void *thread1(void *arg) { int tsd = 0,i=10; pthread_t thid2; printf("thread1 %x is running \n",pthread_self()); while(i>0) { write_globalnumber(); i--; usleep(1); } } int main(void) { pthread_t thid1,thid2; int statue; printf("main thread begins running\n"); pthread_create(&thid1,NULL,thread1,NULL); pthread_create(&thid2,NULL,thread2,NULL); pthread_join(thid1,&statue); printf("statue1=%d\n",statue); pthread_join(thid2,&statue); printf("statue2=%d\n",statue); printf("main thread exit\n"); return 0; }程序运行结果: 每次运行的会不一样。因为线程的调度是受系统控制的,不是按顺序调度的。一个有趣的现象是 当数据变量是3,为什么输出是2呢?因为读线程正准备打印2的时候被中断了,然后执行了写线程写入3。所以,互斥锁做的同步是保证加锁的部分所用到的资源,在同一时刻只有一个线程能用到。如果把输出语句放到加锁的部分,就可以看到读出来的和写出来的,是按顺序出来的。
2.条件变量 测试代码
#include <stdio.h> #include <unistd.h> #include <pthread.h> pthread_mutex_t mutex; pthread_cond_t cond; void * thread1(void *arg) { pthread_cleanup_push(pthread_mutex_unlock,&mutex); while(1) { printf("thread1 is running\n"); pthread_mutex_lock(&mutex); printf("thread1 mutex\n"); pthread_cond_wait(&cond,&mutex); printf("thread1 applied the condition\n"); pthread_mutex_unlock(&mutex); sleep(4); } pthread_cleanup_pop(0); } void *thread2(void *arg) { while(1) { printf("thread2 is running\n"); pthread_mutex_lock(&mutex); printf("thread2 mutex\n"); pthread_cond_wait(&cond,&mutex); printf("thread2 applied the condition\n"); pthread_mutex_unlock(&mutex); sleep(1); } } int main() { pthread_t tid1,tid2; printf("condition variable study\n"); pthread_mutex_init(&mutex,NULL); pthread_cond_init(&cond,NULL); pthread_create(&tid1,NULL,(void *)thread1,NULL); pthread_create(&tid2,NULL,(void *)thread2,NULL); do{ pthread_cond_signal(&cond); }while(1); sleep(50); pthread_exit(0); }上面代码加了“pthread_cleanup_push(pthread_mutex_unlock,&mutex);”和“pthread_cleanup_pop(0);”这对函数,目的是解决线程结束时资源释放的问题。因为pthread_cond_wait函数属于“取消点”。其实对于thread2也应该设置这对函数。他们在线程出现意外的时候,才会使用到。具体,请看我另一篇关于“线程终止的博文”
3.异步信号
错误码定义在“error.h”以E开头的宏定义。 常见的错误码
错误码含义ENOMEM内存不足EIO输入输出错误