本章内容:一线程的基本函数:二.线程同步与互斥
一线程的基本函数:
1.intpthread_attr_init(pthread_attr_t* attr); 线程属性初始化
2.intpthread_attr_destory(pthread_addr_t* attr); 线程属性注销
3.Intpthread_attr_setdetachstat(const pthread_attr_t* attr, int detachstate);设置分离属性
4.Intpthread_attr_getdetachstat(const pthread_attr_t* restrict attr, int*detachstate);获取分离属性
5.Intpthread_creat(pthread_t* thread,const pthread_attr_t* attr, void*(*start_run)(void*),void* arg);创建线程thread线程标识attr线程属性设置start_run线程函数的地址arg传递给start_run的参数
6.Intpthread_exit(void* retval);自己关闭自己retval线程结束时的返回值,可由pthread_jion来获取
7.Intpthread_jion(pthread_th id, void** thread_return);取退出的线程的返回值,并清理子线程的资源id等待的线程的标实 thread_return用户自定义指针,用来存储线程pthread_exit函数的返回值
8.Intphread_cancel(pthread_t id);取消同一进程中的其他线程成功返回0;出错返回错误代码
9.Pthread_tpthread_self();获取当前线程ID
10.Intpthread_equal(pthread_t tid1,pthread_t tid2);比较两个线程,相等返回非0,否则返回0
例子1:phread_cancel的使用
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <pthread.h> 4 5 void *thrd_func1(void *arg); 6 void *thrd_func2(void *arg); 7 8 pthread_t tid1,tid2; 9 10 int main(){ 11 // 创建线程tid1,线程函数thrd_func1 12 if (pthread_create(&tid1,NULL,thrd_func1,NULL)!=0) { 13 printf("Create thread 1 error!\n"); 14 exit(1); 15 } 16 // 创建线程tid2,线程函数thrd_func2 17 if (pthread_create(&tid2,NULL,thrd_func2,NULL)!=0) { 18 printf("Create thread 2 error!\n"); 19 exit(1); 20 } 21 // 等待线程tid1退出 22 if (pthread_join(tid1,NULL)!=0){ 23 printf("Join thread 1 error!\n"); 24 exit(1); 25 }else 26 printf("Thread 1 Joined!\n"); 27 // 等待线程tid2退出 28 if (pthread_join(tid2,NULL)!=0){ 29 printf("Join thread 2 error!\n"); 30 exit(1); 31 }else 32 printf("Thread 2 Joined!\n"); 33 34 return 0; 35 } 36 37 void *thrd_func1(void *arg) { 38 // pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); 39 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); // 设置其他线程可以cancel掉此线程 40 41 while(1) { 42 printf("Thread 1 is running!\n"); 43 sleep(1); } 45 pthread_exit((void *)0); 46 } 47 48 void *thrd_func2(void *arg) { 49 printf("Thread 2 is running!\n"); 50 sleep(5); 51 if (pthread_cancel(tid1)==0) // 线程tid2向线程tid1发送cancel 52 printf("Send Cancel cmd to Thread 1.\n"); pthread_exit((void *)0); }
例2:pthread_exit的使用
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <pthread.h> 4 5 #define THREAD_NUM 3 6 #define REPEAT_TIMES 5 7 #define DELAY 4 8 9 void *thrd_func(void *arg); 10 11 int main(){ 12 pthread_t thread[THREAD_NUM]; 13 int no; 14 void *tret;15 16 srand((int)time(0)); // 初始化随机函数发生器 17 18 for(no=0;no<THREAD_NUM;no++){ 19 if (pthread_create(&thread[no],NULL,thrd_func,(void*)no)!=0) { // 创建THREAD_NUM个线程,传入(void*)no作为thrd_func的参数 20 printf("Create thread %d error!\n",no); 21 exit(1); 22 } else 23 printf("Create thread %d success!\n",no); 24 } 25 26 for(no=0;no<THREAD_NUM;no++){ 27 if (pthread_join(thread[no],&tret)!=0){ // 等待thread[no]线程结束,线程函数返回值放在tret中 28 printf("Join thread %d error!\n",no); 29 exit(1); 30 }else 31 printf("Join thread %d success!\n",no); 32 } 33 34 return 0; 35 } 36 37 void *thrd_func(void *arg){ 38 int thrd_num=(void*)arg; 39 int delay_time=0; 40 int count=0; 41 42 printf("Thread %d is starting.\n",thrd_num); 43 for(count=0;count<REPEAT_TIMES;count++) { 44 delay_time=(int)(DELAY*(rand()/(double)RAND_MAX))+1; 45 sleep(delay_time); 46 printf("\tThread %d:job %d delay =%d.\n",thrd_num,count,delay_time); 47 } 48 49 printf("Thread %d is exiting.\n",thrd_num); 50 pthread_exit(NULL); 51 }
例子3:
1.结合式创建的线程;线程结束时需要调用pthread_jion来回收子线程的资源
2.分离式创建的线程;线程结束时不需要调用pthread_jion来回收子线程的资源,子线程会自己去回收资源,但是无法获得线程结束的返回值
Struct teacher {
Int age; Char sex;
}
Void* pthread_mian(void* arg){
Struct teacher *tea = arg;
Whiel(1) {
Printf(“age=%d,sex=%c\n”,tea->age,tea->sex);
Sleep(2);
}
Pthread_exit((void*)100);//主动退出
Return (void*)100; //自动退出
}
int main()
{
Struct teacher tea;
T ea.age=40;
Tea.sex=’M’;
Pthread_attr_t attr;//线程属性
Pthread_attr_int(&attr);//初始化结构体
pthread_attr_setdetachstat(&attr,PTHREAD_CREATE_DETACHED); //分离状态
Pthread_t pthreadid=0;
pthread_creat(&pthreadid,&attr, pthread_mian,& tea);
//while(1)
//{sleep(2);}
for(int i=0;i<10;i++)
{ sleep(2); }
Printf(“主线程ID=%d,子线程ID=%d”,pthread_self(),pthreadid);
Pthread_cancel( pthreadid );
Printf(“主线程ID=%d,子线程ID=%d”,pthread_self(),pthreadid);
Sleep(5);// Pthread_jion会阻塞等待子线程退出,不需要sleep
Int res=0;
//Pthread_jion( pthreadid,(void**)&res);//取退出的线程的返回值,并清理子线程的资源 上面设置里分离属性
Printf(“%d\n”,res);
Pthread_attr_destroy(&attr);//线程属性注销
return 0;
}
编译:gcc .... –lpthread
Gcc...... –std=c99(int定义到for循环里)
线程退出
二.线程同步与互斥
1.基本概念
线程共享进程的资源和地址空间,对这些资源进行操作时,必须考虑线程间同步与互斥问题
三种线程同步机制
•互斥锁
•信号量
•条件变量
互斥锁更适合同时可用的资源是惟一的情况
信号量更适合同时可用的资源为多个的情况
2互斥锁
2.1只有两种状态: 上锁、解锁
2.2互斥锁分类:区别在于其他未占有互斥锁的线程在希望得到互斥锁时是否需要阻塞等待
快速互斥锁
•调用线程会阻塞直至拥有互斥锁的线程解锁为止
•默认为快速互斥锁
检错互斥锁
•为快速互斥锁的非阻塞版本,它会立即返回并返回一个错误信息
2.3互斥锁主要包括下面的基本函数:
互斥锁初始化:pthread_mutex_init()
消除互斥锁:pthread_mutex_destroy()
互斥锁上锁:pthread_mutex_lock()
互斥锁判断上锁:pthread_mutex_trylock()
互斥锁解锁:pthread_mutex_unlock()
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4
5 #define THREAD_NUM 3
6 #define REPEAT_TIMES 5
7 #define DELAY 4
8
9 pthread_mutex_t mutex;
10
11 void *thrd_func(void *arg);
12
13 int main(){
14 pthread_t thread[THREAD_NUM];
15 int no;
16 void *tret;
17
18 srand((int)time(0));
19 // 创建快速互斥锁(默认),锁的编号返回给mutex
20 pthread_mutex_init(&mutex,NULL);
21
22 // 创建THREAD_NUM个线程,每个线程号返回给&thread[no],每个线程的入口函数均为thrd_func,参数为
23 for(no=0;no<THREAD_NUM;no++){
24 if (pthread_create(&thread[no],NULL,thrd_func,(void*)no)!=0) {
25 printf("Create thread %d error!\n",no);
26 exit(1);
27 } else
28 printf("Create thread %d success!\n",no);
29 }
30
31 // 对每个线程进行join,返回值给tret
32 for(no=0;no<THREAD_NUM;no++){
33 if (pthread_join(thread[no],&tret)!=0){
34 printf("Join thread %d error!\n",no);
35 exit(1);
36 }else
37 printf("Join thread %d success!\n",no);
38 }
39 // 消除互斥锁
40 pthread_mutex_destroy(&mutex);
41 return 0;
42 }
43
44 void *thrd_func(void *arg){
45 int thrd_num=(void*)arg; // 传入的参数,互斥锁的编号
46 int delay_time,count;
47
48 // 对互斥锁上锁
49 if(pthread_mutex_lock(&mutex)!=0) {
50 printf("Thread %d lock failed!\n",thrd_num);
51 pthread_exit(NULL);
52 }
53
54 printf("Thread %d is starting.\n",thrd_num);
55 for(count=0;count<REPEAT_TIMES;count++) {
56 delay_time=(int)(DELAY*(rand()/(double)RAND_MAX))+1;
57 sleep(delay_time);
58 printf("\tThread %d:job %d delay =%d.\n",thrd_num,count,delay_time);
59 }
60
61 printf("Thread %d is exiting.\n",thrd_num);
62 // 解锁
63 pthread_mutex_unlock(&mutex);
64
65 pthread_exit(NULL);
66 }
和上一版本的程序差异在于有没有锁,有锁的情况下,必须等"thread x is exiting."之后其他线程才能继续。
3. 信号量
3.1 基本概念
操作系统中所用到的PV原子操作,广泛用于进程或线程间的同步与互斥
•本质上是一个非负的整数计数器,被用来控制对公共资源的访问
PV原子操作:对整数计数器信号量sem的操作
•一次P操作使sem减一,而一次V操作使sem加一
•进程(或线程)根据信号量的值来判断是否对公共资源具有访问权限
–当信号量sem的值大于等于零时,该进程(或线程)具有公共资源的访问权限
–当信号量sem的值小于零时,该进程(或线程)就将阻塞直到信号量sem的值大于等于0为止
PV操作主要用于线程间的同步和互斥
互斥,几个线程只设置一个信号量sem
同步,会设置多个信号量,安排不同初值来实现它们之间的顺序执行
3.2信号量函数
sem_init() 创建一个信号量,并初始化它
sem_wait()和sem_trywait(): P操作,在信号量大于零时将信号量的值减一
•区别: 若信号量小于零时,sem_wait()将会阻塞线程,sem_trywait()则会立即返回
sem_post(): V操作,将信号量的值加一同时发出信号来唤醒等待的线程
sem_getvalue(): 得到信号量的值
sem_destroy(): 删除信号量
eg.同步各线程,执行顺序为逆序。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4 #include <semaphore.h>
5
6 #define THREAD_NUM 3
7 #define REPEAT_TIMES 5
8 #define DELAY 4
9
10 sem_t sem[THREAD_NUM];
11
12 void *thrd_func(void *arg);
13
14 int main(){
15 pthread_t thread[THREAD_NUM];
16 int no;
17 void *tret;
18
19 srand((int)time(0));
20
21 // 初始化THREAD_NUM-1个信号量,均初始化为0
22 for(no=0;no<THREAD_NUM-1;no++){
23 sem_init(&sem[no],0,0); //sem[0] sem[1]
24 }
25
26 // sem[2]信号量初始化为1,即sem数组中最后一个信号量
27 sem_init(&sem[2],0,1);
28
29 // 创建THREAD_NUM个线程,入口函数均为thrd_func,参数为(void*)no
30 for(no=0;no<THREAD_NUM;no++){
31 if (pthread_create(&thread[no],NULL,thrd_func,(void*)no)!=0) {
32 printf("Create thread %d error!\n",no);
33 exit(1);
34 } else
35 printf("Create thread %d success!\n",no);
36 }
37
38 // 逐个join掉THREAD_NUM个线程
39 for(no=0;no<THREAD_NUM;no++){
40 if (pthread_join(thread[no],&tret)!=0){
41 printf("Join thread %d error!\n",no);
42 exit(1);
43 }else
44 printf("Join thread %d success!\n",no);
45 }
46
47 // 逐个取消信号量
48 for(no=0;no<THREAD_NUM;no++){
49 sem_destroy(&sem[no]);
50 }
51
52 return 0;
53 }
54
55 void *thrd_func(void *arg){
56 int thrd_num=(void*)arg; // 参数no
57 int delay_time,count;
58
59 // 带有阻塞的p操作
60 sem_wait(&sem[thrd_num]);
61
62
63 printf("Thread %d is starting.\n",thrd_num);
64 for(count=0;count<REPEAT_TIMES;count++) {
65 delay_time=(int)(DELAY*(rand()/(double)RAND_MAX))+1;
66 sleep(delay_time);
67 printf("\tThread %d:job %d delay =%d.\n",thrd_num,count,delay_time);
68 }
69
70 printf("Thread %d is exiting.\n",thrd_num);
71
72 // 对前一个信号量进行V操作
73 // 由于只有最后一个信号量初始化为1,其余均为0
74 // 故线程执行的顺序将为逆序
75 sem_post(&sem[(thrd_num+THREAD_NUM-1)%THREAD_NUM]); //V操作
76
77 pthread_exit(NULL); // 线程主动结束
78 }
阶段回顾:
线程的概念
Linux中线程的实现
线程创建和终止
线程清理和控制函数
线程的状态
线程属性初始化
分离属性
互斥和同步