pthread_exit和pthread_join进一步说明: 3
37.多线程链表添加删除例子(使用条件变量实现互斥): 32
注:线程的使用需要包含: #include <pthread.h>
1.创建缺省线程
int pthread_create(pthread_t *tid, pthread_attr_t *tattr,
void*(*start_routine)(void *), void *arg);
参数:
当pthread_create() 成功时,所创建线程的ID 被存储在由tid 指向的位置中。第二个参数用于设置线程属性,如果不需要特殊的属性,可以简单的设置该参数为NULL,最后两个参数告诉线程将要启动执行的函数和传递给该函数的参数。
返回值:
调用成功完成后返回0,其他的值都表示出现错误。
如果检测到以下任一情况,pthread_create() 将失败并返回相应的值。
EAGAIN 超出了系统限制,如创建的线程太多。
EPERM 调用者没有适当的权限设置所需的参数或安排调度策略
EINVAL 描述: tattr 的值无效。(设置的属性有问题)
默认属性:绑定,非分离,继承创建者线程中定义的调度策略。
2.终止线程
void pthread_exit(void *status);
本函数可用来终止调用线程。将释放调用线程所有特定数据绑定。如果调用线程尚未分离,则线程ID 和status 指定的退出状态将保持不变,直到应用程序调用pthread_join() 以等待该线程。否则,将忽略status。线程ID 可以立即回收。
有一个重要的特殊情况,即当初始线程(即调用main() 的线程)从main() 调用返回时或调用exit() 时,整个进程及其所有的线程将终止。因此,一定要确保初始线程不会从main()过早地返回,在其它线程调用exit()也会终止整个进程。
请注意,如果主线程仅仅调用了pthread_exit,则仅主线程本身终止。进程及进程内的其他线程将继续存在。所有线程都已终止时,进程也将终止。
3. 等待线程终止
int pthread_join(thread_t tid, void **status);
参数:
参数tid指定要等待的线程ID,指定的线程必须位于当前的进程中,而且不得是分离线程。
当参数status 不是NULL 时,status 指向某个位置,在pthread_join() 成功返回时,将该位置设置为已终止线程的退出状态。
返回值:
调用成功完成后,pthread_join() 将返回零。其他任何返回值都表示出现了错误。如果检测到以下任一情况,pthread_join() 将失败并返回相应的值。
ESRCH 描述: 没有找到与给定的线程ID 相对应的线程。
EDEADLK 描述: 将出现死锁,如一个线程等待其本身,或者线程A和线程B 互相等待。
EINVAL 描述: 与给定的线程ID 相对应的线程是分离线程。
说明:
如果多个线程等待同一个线程终止,则所有等待线程将一直等到目标线程终止。然后,一个等待线程成功返回。其余的等待线程将失败并返回ESRCH 错误。
pthread_join() 仅适用于非分离的目标线程。如果没有必要等待特定线程终止之后才进行其他处理,则应当将该线程分离。
pthread_exit和pthread_join进一步说明:(一下主线程是main线程)
1.线程自己运行结束,或者调用pthread_exit()结束,线程都会释放自己独有的空间资源。
2.如果线程是非分离的,线程会保留线程id号,直到其他线程通过"joining"这个线程确认其已死亡。join 的结果是joining 线程得到已终止线程的退出状态,已终止的线程将消失。
3.如果线程是分离的,不需要使用pthread_exit(),线程自己运行结束,就会释放所有资源(包括线程id号)。
4.子线程最终一定要使用pthread_join()或者设置为分离状态来结束线程,否则线程的资源不会被完全释放。(使用取消线程功能也不能完全释放),线程id依然存在。
5.主线程运行pthread_exit(),会结束主线程,但不会结束子线程。
6.主线程结束,则整个程序结束,所以主线程最好要使用join等待子线程运行结束。使用join一个线程可以等待多个线程结束。
7.使用join的线程将会阻塞,直到被join的线程结束,join函数返回,但是它对被join的线程运行没有影响。
8.如果子线程使用exit()则可以结束整个进程
9.由于pthread_create会立即出发子线程的执行,那么对于如下代码的解析来看:在main函数内:
pthread_create(&tid, NULL, start_routine, (void *)c);
printf("pthread id :%u\n", tid);
printf("pthread id :%u\n", tid);
printf("pthread id :%u\n", tid);
printf("pthread id :%u\n", tid);
sleep(10);
pthread_join(tid, (void **)&d);
printf("haha \n");
那么在执行pthread_join前,其实start_routine的从线程就已经结束了。Pthread_join只是标识,在pthread_join后的代码需要等到从线程tid执行完毕后才执行,如果tid未执行,则一直处于等待,如果在join前已经结束,则直接执行join后的代码,join本身只负责阻塞,不负责再次运行tid从线程。。这里特别注意,我原来以为从线程tid还重新执行一次呢!!!真是搞笑。。。哈哈
例子:
1 #include <stdlib.h>
2 #include <pthread.h>
3 #include <stdio.h>
4 #include <string.h>
5 void * start_routine(void *arg)
6 {
7 char * a;
8 printf("thread runing,%s\n", (char *)arg);
9 a = malloc(sizeof(char)*6);
10 memset(a, 'a', 5);
11 a[5] = '\0';
12 pthread_exit((void *)a);
13 }
14 int main(int argc, char *argv[])
15 {
16 pthread_t tid;
17 char c[4], *d;
18 memset(c, 'c', 3);
19 c[3] = '\0';
20 pthread_create(&tid, NULL, start_routine, (void *)c);
21 printf("pthread id :%u\n", tid);
22 pthread_join(tid, (void **)&d);
23 printf("main: %s\n", d);
24 free(d);
25 return 0;
26 }
运行结果:
pthread id :3077950352
thread runing,ccc
main: aaaaa
非分离线程未使用join函数例子:
1 #include <stdlib.h>
2 #include <pthread.h>
3 #include <stdio.h>
4 #include <sched.h>
5 #include <errno.h>
6 void *consumer(void *p)
7 {
8 static a = 0;
9 a++;
10 printf("<<<<<<<<<<<<<<<<<<<<<(%u),%d\n", (unsigned)pthread_self(), a);
11 pthread_exit(NULL);
12 }
13 int main(int argc, char *argv[])
14 {
15 pthread_t t1, t2, t3;
16 int ret;
17 do{
18 ret = pthread_create(&t1, NULL, consumer, NULL);
19 if(ret != 0)
20 {
21 printf("create failed,%d\n", ret);
22 exit(1);
23 }
24 }while(1);
25 sleep(1);
26 return 0;
27 }
运行结果:
创建350个左右的线程后,就不能再创建线程,已创建的线程线程号都不同。因为从线程只能通过主线程进行回收,在从线程中使用pthread_exit(NULL);并未完全回收从线程资源,线程编号依然存在。
<<<<<<<<<<<<<<<<<<<<<(33799056),362
<<<<<<<<<<<<<<<<<<<<<(25406352),363
<<<<<<<<<<<<<<<<<<<<<(17013648),364
<<<<<<<<<<<<<<<<<<<<<(8620944),365
create failed,12
如果主函数加上pthread_join(t1, NULL);才可以一直创建线程,如下,
13 int main(int argc, char *argv[])
14 {
15 pthread_t t1, t2, t3;
16 int ret;
17 do{
18 ret = pthread_create(&t1, NULL, consumer, NULL);
19 if(ret != 0)
20 {
21 printf("create failed,%d\n", ret);
22 exit(1);
23 }
24 pthread_join(t1, NULL);
25 }while(1);
26 sleep(1);
27 return 0;
28 }
部分结果如下:
<<<<<<<<<<<<<<<<<<<<<(3076656016),15002
<<<<<<<<<<<<<<<<<<<<<(3076656016),15003
<<<<<<<<<<<<<<<<<<<<<(3076656016),15004
<<<<<<<<<<<<<<<<<<<<<(3076656016),15005
<<<<<<<<<<<<<<<<<<<<<(3076656016),15006
<<<<<<<<<<<<<<<<<<<<<(3076656016),15007
可见线程id号被回收,并被用于创建新的线程。
没有pthread_exit(),只有pthread_join()线程资源也能释放:
6 void *consumer(void *p)
7 {
8 static a = 0;
9 a++;
10 printf("<<<<<<<<<<<<<<<<<<<<<(%u),%d\n", (unsigned)pthread_self(), a);
11 }
12 int main(int argc, char *argv[])
13 {
14 pthread_t t1, t2, t3;
15 int ret;
16 do{
17 ret = pthread_create(&t1, NULL, consumer, NULL);
18 if(ret != 0)
19 {
20 printf("create failed,%d\n", ret);
21 exit(1);
22 }
23 pthread_join(t1, NULL);
24 }while(1);
25 sleep(1);
26 return 0;
27 }
输出结果如下:
<<<<<<<<<<<<<<<<<<<<<(3076357008),19997
<<<<<<<<<<<<<<<<<<<<<(3076357008),19998
<<<<<<<<<<<<<<<<<<<<<(3076357008),19999
只使用pthread_detach()分离子线程例子:
6 void *consumer(void *p)
7 {
8 static a = 0;
9 a++;
10 printf("<<<<<<<<<<<<<<<<<<<<<(%u),%d\n", (unsigned)pthread_self(), a);
11 pthread_detach(pthread_self());
12 return NULL;
13 }
14 int main(int argc, char *argv[])
15 {
16 pthread_t t1, t2, t3;
17 int ret;
18 do{
19 ret = pthread_create(&t1, NULL, consumer, NULL);
20 if(ret != 0)
21 {
22 printf("create failed,%d\n", ret);
23 exit(1);
24 }
25 }while(1);
26
27 return 0;
28 }
运行结果:只能创建一千个左右的线程,如下
<<<<<<<<<<<<<<<<<<<<<(3128908688),1184
<<<<<<<<<<<<<<<<<<<<<(3137301392),1185
<<<<<<<<<<<<<<<<<<<<<(3145694096),1186
create failed,12
分析原因可能是主线程创建线程速度太快,子线程还来不及释放资源,最终导致资源不足,下面在主函数里面加一个printf(),就不会出问题了。
14 int main(int argc, char *argv[])
15 {
16 pthread_t t1, t2, t3;
17 int ret;
18 do{
19 ret = pthread_create(&t1, NULL, consumer, NULL);
20 if(ret != 0)
21 {
22 printf("create failed,%d\n", ret);
23 exit(1);
24 }
25 printf("<<<<<<<<<<<<<<<");
26 }while(1);
27
28 return 0;
29 } 部分结果如下:
<<<<<<<<<<<<<<<<<<<<<(2539965328),15210
<<<<<<<<<<<<<<<<<<<<<(3060312976),15211
<<<<<<<<<<<<<<<<<<<<<(3051920272),15212
<<<<<<<<<<<<<<<<<<<<<(3009956752),15213
总结如下:
1.线程自己运行结束,或者调用pthread_exit()结束,线程都会释放自己独立的空间资源。
2.如果线程是非分离的,线程会保留线程id号,直到其他线程通过"joining"这个线程确认其已死亡。join 的结果是joining 线程得到已终止线程的退出状态,已终止的线程将消失。
4.分离线程
int pthread_detach(thread_t tid);
函数用于指示应用程序在线程tid 终止时回收其存储空间。如果tid 尚未终
止,pthread_detach() 不会终止该线程。
返回值:
pthread_detach() 在调用成功完成之后返回零。其他任何返回值都表示出现了错误。如果检测到以下任一情况,pthread_detach() 将失败并返回相应的值。
EINVAL 描述: tid 是分离线程。
ESRCH 描述: tid 不是当前进程中有效的未分离的线程。
注意:为了避免线程的资源在线程结束时不能得到正确释放,从而避免产生潜在的内存泄漏问题,在对待线程结束时,要确保该线程处于detached 状态,否则就需要调用 pthread_join() 函数来对其进行资源回收。(如果不进行以上操作,线程id号不会被回收)。但是如果主线程结束,整个程序就结束了,所以最好在主线程使用,join等待其它线程结束。
5.获取线程标识符
pthread_t pthread_self(void);
返回调用线程的标识符,(是一个无符号整形数)
6.比较线程ID
int pthread_equal(pthread_t tid1, pthread_t tid2);
如果tid1 和tid2 相等,pthread_equal() 将返回非零值,否则将返回零。如果tid1 或tid2 是无效的线程标识号,则结果无法预测。
7. 一次性初始化,这个函数粗暴的完成了线程的同步,哈哈
int pthread_once(pthread_once_t *once_control, void (*init_routine) (void))
本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。
例子:
1 #include <stdlib.h>
2 #include <pthread.h>
3 #include <stdio.h>
4 #include <string.h>
5 pthread_once_t once = PTHREAD_ONCE_INIT;
6
7 void once_run(void)
8 {
9 printf("once_run in thread %u\n ",(unsigned int )pthread_self());
10 }
11
12 void * child1(void * arg)
13 {
14 pthread_t tid =pthread_self();
15 printf("thread: %u enter\n", tid);
16 pthread_once(&once,once_run);
17 printf("thread %u return\n", tid);
18 }
19
20
21 void * child2(void * arg)
22 {
23 pthread_t tid =pthread_self();
24 printf("thread: %u enter\n", tid);
25 pthread_once(&once,once_run);
26 printf("thread %u return\n", tid);
27 }
28
29 int main(void)
30 {
31 pthread_t tid1,tid2;
32 printf("hello\n");
33 pthread_create(&tid1,NULL,child1,NULL);
34 pthread_create(&tid2,NULL,child2,NULL);
35 sleep(5);
36 printf("main thread exit\n");
37 return 0;
38 }
运行结果:
hello
thread: 3067587472 enter
once_run in thread 3067587472 —这里可以看到,once_run(void)这个函数只运行了一次
thread 3067587472 return
thread: 3075980176 enter
thread 3075980176 return
main thread exit
8. 设置线程的调度策略和优先级——关于线程的优先级暂时就不用看了
int pthread_setschedparam(pthread_t tid, int policy,
const struct sched_param *param);
用于设置现有线程的调用策略和优先级。
参数:
tid需要设置的线程id号,
policy 调度策略,
param 优先级。
线程的调度有三种策略:SCHED_OTHER、SCHED_RR和SCHED_FIFO
SCHED_OTHER
它是默认的线程分时调度策略,所有的线程的优先级别都是0,线程的调度是通过分时来完成的。简单地说,如果系统使用这种调度策略,程序将无法设置线程的优先级。请注意,这种调度策略也是抢占式的,当高优先级的线程准备运行的时候,当前线程将被抢占并进入等待队列。这种调度策略仅仅决定线程在可运行线程队列中的具有相同优先级的线程的运行次序。
SCHED_FIFO
它是一种实时的先进先出调用策略,且只能在超级用户下运行。这种调用策略仅仅被使用于优先级大于0的线程。它意味着,使用SCHED_FIFO的可运行线程将一直抢占使用SCHED_OTHER的运行线程J。此外SCHED_FIFO是一个非分时的简单调度策略,当一个线程变成可运行状态,它将被追加到对应优先级队列的尾部((POSIX 1003.1)。当所有高优先级的线程终止或者阻塞时,它将被运行。对于相同优先级别的线程,按照简单的先进先运行的规则运行。我们考虑一种很坏的情况,如果有若干相同优先级的线程等待执行,然而最早执行的线程无终止或者阻塞动作,那么其他线程是无法执行的,除非当前线程调用如pthread_yield之类的函数,所以在使用SCHED_FIFO的时候要小心处理相同级别线程的动作。
SCHED_RR
鉴于SCHED_FIFO调度策略的一些缺点,SCHED_RR对SCHED_FIFO做出了一些增强功能。从实质上看,它还是SCHED_FIFO调用策略。它使用最大运行时间来限制当前进程的运行,当运行时间大于等于最大运行时间的时候,当前线程将被切换并放置于相同优先级队列的最后。这样做的好处是其他具有相同级别的线程能在“自私“线程下执行。
例子:
1 #include <stdlib.h>
2 #include <pthread.h>
3 #include <stdio.h>
4 #include <sched.h>
5
6 void *consumer(void *p)
7 {
8 int i;
9 printf("start (%d)\n", (int)p);
10 for (i = 0; 1; i++)
11 {
12 if(i%200 == 10)
13 printf("<<<<<<<<<<<<<<<<<<<<<(%d)\n", (int)p);
14
15 }
16 }
17 int main(int argc, char *argv[])
18 {
19 pthread_t t1, t2, t3;
20 struct sched_param sched3;
21 sched3.__sched_priority = 70;
22
23 pthread_create(&t1, NULL, consumer, (void *)4);
24 pthread_create(&t2, NULL, consumer, (void *)5);
25 pthread_create(&t3, NULL, (consumer), (void *)6);
26
27 sleep(8);
28 pthread_setschedparam(t3, SCHED_FIFO, &sched3);
29
30 pthread_join(t1, NULL);
31 pthread_join(t2, NULL);
32 pthread_join(t3, NULL);
33 return 0;
34 }
35
运行结果:
前8秒交替打印 <<<<<<<<<<<<<<<<<<<<<(4) 和<<<<<<<<<<<<<<<<<<<<<(5)
和 <<<<<<<<<<<<<<<<<<<<<(6)
8秒以后只打印 <<<<<<<<<<<<<<<<<<<<<(6)
注意:如果t3线程用sleep()阻塞自己,其它线程将会被调度执行。
上面的程序在8秒后主线程(执行main)也不会执行,比如在29行增加一个exit(1),如果主线程会执行到29行,整个程序将结束,但实际上还会一直执行t3线程.主线程不会被执行。
9. 获取线程的优先级
int pthread_getschedparam(pthread_t thread, int *restrict policy,
struct sched_param *restrict param);
函数在成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
ESRCH 描述: tid 指定的值不引用现有的线程。
例子:
1 #include <stdlib.h>
2 #include <pthread.h>
3 #include <stdio.h>
4 #include <sched.h>
5
6 void *consumer(void *p)
7 {
8 int i;
9 printf("start (%d)\n", (int)p);
10 for (i = 0; 1; i++)
11 {
12 sleep(1);
13 printf("<<<<<<<<<<<<<<<<<<<<<wake(%d)\n", (int)p);
14
15 }
16 }
17 int main(int argc, char *argv[])
18 {
19 pthread_t t1, t2, t3;
20 struct sched_param sched3, sched4;
21 sched3.__sched_priority = 70;
22 int policy;
23
24 pthread_create(&t1, NULL, consumer, (void *)4);
25 pthread_create(&t2, NULL, consumer, (void *)5);
26 pthread_create(&t3, NULL, (consumer), (void *)6);
27
28 sleep(4);
29 pthread_setschedparam(t3, SCHED_FIFO, &sched3);
30 printf("main run\n");
31 pthread_getschedparam(t3, &policy, &sched4);
32 printf("policy: %d, priority: %d\n", policy, sched4.__sched_priority);
33 exit(1);
34
35 pthread_join(t1, NULL);
36 pthread_join(t2, NULL);
37 pthread_join(t3, NULL);
38 return 0;
39 }
运行结果:
start (6)
start (5)
start (4)
<<<<<<<<<<<<<<<<<<<<<wake(6)
<<<<<<<<<<<<<<<<<<<<<wake(5)
<<<<<<<<<<<<<<<<<<<<<wake(4)
<<<<<<<<<<<<<<<<<<<<<wake(6)
<<<<<<<<<<<<<<<<<<<<<wake(5)
<<<<<<<<<<<<<<<<<<<<<wake(4)
<<<<<<<<<<<<<<<<<<<<<wake(6)
<<<<<<<<<<<<<<<<<<<<<wake(5)
<<<<<<<<<<<<<<<<<<<<<wake(4)
main run
policy: 1, priority: 70
10.取消线程
int pthread_cancel(pthread_t thread);
设置取消点
void pthread_testcancel(void);
测试是否接收到取消请求,如果有,结束线程。
例子:
9 int a = 0;
10 void *thread1(void *arg)
11 {
12 pthread_testcancel();
13 a = 10;
14 }
15 int main(int argc, char *argv[])
16 {
17 pthread_t t1, t2, t3;
18 int ret, i;
19 printf("main start\n");
20 ret = pthread_create(&t1, NULL, thread1, NULL);
21 pthread_cancel(t1);
22 pthread_join(t1, NULL);
23 sleep(3);
24 printf("main end, a=%d\n", a);
25 return 0;
26 }
运行结果:在取消点处程序结束,a值未该。
main start
main end, a=0
如果改为:
9 int a = 0;
10 void *thread1(void *arg)
11 {
12 a = 10;
13 pthread_testcancel();
14 }
运行结果:a值被修改了
main start
main end, a=10
9 void *thread1(void *arg)
10 {
11 printf("start thread (%u)\n", (unsigned)pthread_self());
12 printf("thread (%u) end\n", (unsigned)pthread_self());
13 }
14 int main(int argc, char *argv[])
15 {
16 pthread_t t1, t2, t3;
17 int ret;
18 printf("main start\n");
19 ret = pthread_create(&t1, NULL, thread1, NULL);
20 if(ret != 0)
21 {
22 printf("create thread failed\n");
23 exit(1);
24 }
25 pthread_cancel(t1);
26 sleep(5);
27 printf("main end\n");
28 return 0;
29 }
运行结果:
main start
start thread (3076238224)
main end
注意:子线程并没设置取消点,但是却被取消了,原因是printf函数包含了一个个取消点(应该在函数结尾),在取消点检测到取消请求时结束线程,第二个printf不会运行,如果在printf前加一个取消点,线程在printf运行前被取消了,不会有结果输出。如下:
9 void *thread1(void *arg)
10 {
11 pthread_testcancel();
12 printf("start thread (%u)\n", (unsigned)pthread_self());
13 printf("thread (%u) end\n", (unsigned)pthread_self());
14 }
运行结果:
main start
main end
取消线程,是否会释放线程的所有资源?例子:
9 void *thread1(void *arg)
10 {
11 printf("start thread (%u)\n", (unsigned)pthread_self());
12 }
13 int main(int argc, char *argv[])
14 {
15 pthread_t t1, t2, t3;
16 int ret;
17 printf("main start\n");
18 do{
19 ret = pthread_create(&t1, NULL, thread1, NULL);
20 if(ret != 0)
21 {
22 printf("create thread failed\n");
23 exit(1);
24 }
25 pthread_cancel(t1);
26 if(ret != 0)
27 {
28 printf("join failed\n");
29 exit(1);
30 }
31 }while(1);
32 return 0;
33 }
运行结果:
start thread (349191056), 327
start th<<<<<<<<<<<<<<<<<<<<<<<create thread failed
注意:每次运行的结果都不一样,在主线程里面加了printf限制产生线程的速度,但是能生成的线程数都在350个左右,应该可以判断,取消并没完全释放资源。所以取消线程后,还应该用join来完全释放资源:如下:
14 int main(int argc, char *argv[])
15 {
16 pthread_t t1, t2, t3;
17 int ret, i;
18 printf("main start\n");
19 do{
20 ret = pthread_create(&t1, NULL, thread1, NULL);
21 if(ret != 0)
22 {
23 printf("create thread failed\n");
24 exit(1);
25 }
26 pthread_cancel(t1);
27 printf("<<<<<<<<<<<<<<<<<<<<<");
28 pthread_join(t1, NULL);
29 if(ret != 0)
30 {
31 printf("join failed\n");
32 exit(1);
33 }
34 }while(1);
35 return 0;
36 }
运行结果:将不断创建新线程.
注意:取消线程相当于使用pthread_exit终止线程。
启用或禁用取消功能:
int pthread_setcancelstate(int state, int *oldstate);
oldstate放置旧的取消状态
pthread_setcancelstate() 在成功完成之后返回零。
例子:
int oldstate;
int ret;
/* enabled */
ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
/* disabled */
ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
设置取消类型
int pthread_setcanceltype(int type, int *oldtype);
type可取的值为PTHREAD_CANCEL_DEFERRED 延迟(默认),就是在取消点结束。
PTHREAD_CANCEL_ASYNCHRONOUS ,可以在执行过程中的任意一点取消线程。
例子:
9 int a = 0;
10
11 void *thread1(void *arg)
12 {
13 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
14 a = 10;
15 printf("start thread (%u)\n", (unsigned)pthread_self());
16 }
17 int main(int argc, char *argv[])
18 {
19 pthread_t t1, t2, t3;
20 int ret, i;
21 printf("main start\n");
22 ret = pthread_create(&t1, NULL, thread1, NULL);
23 if(ret != 0)
24 {
25 printf("create thread failed\n");
26 exit(1);
27 }
28 pthread_cancel(t1);
29 pthread_join(t1, NULL);
30 sleep(3);
31 printf("main end, a=%d\n", a);
32 return 0;
33 }
运行结果: 设置为异步模式,在没到达取消点之前就结束了,a没被改动。如果把13行注释,a会被改动。
main start
main end, a=0
注意:使用取消功能要很注意,很多C库函数(有阻塞性质的)也隐含有取消点,比如sleep(),pthread_cond_wait(),而且,取消线程并不能完全释放线程资源,所以编程的时候尽量不要使用取消功能。
但是,下面这个情况用 异步取消+join 可以达到终止无限循环线程的目的:
9 int a = 0;
10 void *thread1(void *arg)
11 {
12 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
13 do{
14 a++;
15 }while(1);
16 }
17 int main(int argc, char *argv[])
18 {
19 pthread_t t1, t2, t3;
20 int ret, i;
21 printf("main start\n");
22 ret = pthread_create(&t1, NULL, thread1, NULL);
23
24 sleep(1);
25 pthread_cancel(t1);
26 pthread_join(t1, NULL);
27 printf("main end, a=%d\n", a);
28 return 0;
29 }
运行结果:
main start
main end, a=294314990
11.初始化属性
int pthread_attr_init(pthread_attr_t *tattr);
函数将对象属性初始化为其缺省值。可能会分配一些存储空间,所以需要下面的函数删除初始化期间分配的存储空间。
int pthread_attr_destroy(pthread_attr_t *tattr);
以上两个函数成功都返回 0.
6 void *consumer(void *p)
7 {
8 static int a = 0;
9 printf("<<<<<<<<<<<<<<<<<<<<<(%u),%d\n", (unsigned)pthread_self(), ++a);
10 return NULL;
11 }
12 int main(int argc, char *argv[])
13 {
14 pthread_t t1, t2, t3;
15 int ret;
16 pthread_attr_t attr;
17 pthread_attr_init(&attr);
18 ret = pthread_create(&t1, &attr, consumer, NULL);
19 if(ret != 0)
20 {
21 printf("create failed,%d\n", ret);
22 exit(1);
23 }
24 pthread_attr_destroy(&attr);
25 sleep(1);
26 return 0;
27 }
输出结果:<<<<<<<<<<<<<<<<<<<<<(3077286800),1
注意:属性对象必须初始化,否则属性不能生效,创建线程时将返回错误。
属性对象被销毁,并不影响线程的属性。
12.设置分离状态
int pthread_attr_setdetachstate(pthread_attr_t *tattr,int detachstate);
第二个参数有两种取值, PTHREAD_CREATE_DETACHED 分离
PTHREAD_CREATE_JOINABLE 非分离
区别参考上面的 《3. 等待线程终止》
设置的效果和使用函数pthread_detach()是一样的。
13.设置范围
有两种范围: PTHREAD_SCOPE_SYSTEM 或 PTHREAD_SCOPE_PROCESS,但是没看出来设置后的运行效果有什么区别,还有就是linux里面也只有PTHREAD_SCOPE_SYSTEM范围的,不支持PTHREAD_SCOPE_PROCESS,如果设置范围PTHREAD_SCOPE_PROCESS将返回错误。所以编程的时候可以不关心这个设置。
14. 设置继承的调度策略
int pthread_attr_setinheritsched(pthread_attr_t *tattr, int inherit);
inherit 值PTHREAD_INHERIT_SCHED 表示新建的线程将继承创建者线程中定义的调度策略。将忽略在pthread_create() 调用中定义的所有调度属性。如果使用PTHREAD_EXPLICIT_SCHED,则将使用pthread_create() 调用中的属性。
返回值:
成功完成后将返回零。其他任何返回值都表示出现了错误。
如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL 描述: 尝试将tattr 设置为无效的值。
ENOTSUP 描述: 尝试将该属性设置为不受支持的值。
15.设置调度策略
int pthread_attr_setschedpolicy(pthread_attr_t *tattr, int policy);
policy可取SCHED_FIFO(实行先入先出)、SCHED_RR(实时循环)或SCHED_OTHER
它们的区别参看 《8. 设置线程的调度策略和优先级》
它们设置的效果是一样的,不同点在于:这里在线程创建前设置,前者在线程创建后设置。默认调度策略是SCHED_OTHER。
返回值:
成功完成后将返回零。其他任何返回值都表示出现了错误。
如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL 描述: 尝试将tattr 设置为无效的值。
ENOTSUP 描述: 尝试将该属性设置为不受支持的值。
注意:必须同时设置继承的调度策略为PTHREAD_EXPLICIT_SCHED,这里的设置才能生效。
16. 设置调度参数
int pthread_attr_setschedparam(pthread_attr_t *tattr,
const struct sched_param *param);
调度参数是在param 结构中定义的。仅支持优先级参数。新创建的线程使用此优先级运行。
注意:SCHED_FIFO和SCHED_RR,对应的优先级范围是:1到99,99的优先级最高,
如果优先级错误,线程无法创建。要包含头文件sched.h。
例子:主线程和第一个创建的子线程都是普通线程,第二个创建的子线程是实时线程。
4 #include <sched.h>
6 void *consumer(void *p)
7 {
8 do{
9 printf("<<<<<<<<<<<<<<<<<<<<<(%u),%d\n", (unsigned)pthread_self(), (unsigned int)p);
10 }while(1);
11 }
12 int main(int argc, char *argv[])
13 {
14 pthread_t t1, t2, t3;
15 int ret;
16 struct sched_param param;
17 param.sched_priority = 1;
18 pthread_attr_t attr;
19 pthread_attr_init(&attr);
20
21 pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
22 pthread_attr_setschedpolicy(&attr, SCHED_RR);
23 pthread_attr_setschedparam(&attr, ¶m);
24
25 ret = pthread_create(&t1, NULL, consumer,(void *)1);
26 sleep(2);
27 ret = pthread_create(&t2, &attr, consumer,(void *)2);
28 if(ret != 0)
29 {
30 printf("create failed,%d\n", ret);
31 exit(1);
32 }
33 pthread_attr_destroy(&attr);
34 sleep(1);
35 printf("main returned\n");
36 return 0;
37 }
运行结果:
前两秒主线程和第一个子线程交替运行,两秒后一直运行第二个子线程.
例子:
6 void *consumer1(void *p)
7 {
8 do{
9 printf("<<<<<<<<<<<<<<<<<<<<<(%u),%d\n", (unsigned)pthread_self(), 1);
10 }while(1);
11 }
12 void *consumer2(void *p)
13 {
14 do{
15 printf("<<<<<<<<<<<<<<<<<<<<<(%u),%d\n", (unsigned)pthread_self(), 2);
16 struct sched_param sched;
17 sched.__sched_priority = 99;
18 pthread_setschedparam(*(pthread_t *)p, SCHED_FIFO, &sched);
19 }while(1);
20 }
21 int main(int argc, char *argv[])
22 {
23 pthread_t t1, t2, t3;
24 int ret;
25 struct sched_param param;
26 param.sched_priority = 1;
27 pthread_attr_t attr;
28 pthread_attr_init(&attr);
29
30 pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
31 pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
32 pthread_attr_setschedparam(&attr, ¶m);
33
34 ret = pthread_create(&t1, NULL, consumer1, NULL);
35 sleep(2);
36 ret = pthread_create(&t2, &attr, consumer2,(void *)&t1);
37 if(ret != 0)
38 {
39 printf("create failed,%d\n", ret);
40 exit(1);
41 }
42 pthread_attr_destroy(&attr);
43 sleep(1);
44 printf("main returned\n");
45 return 0;
46 }
运行结果:
1.最终一直运行 void *consumer1(void *p),
2.如果把18行注释掉,最终一直运行void *consumer2(void *p),
3.在18行第一个子线程的被改为实时并优先级大于第二个线程,所以它抢占cpu并一直运行。
4.如果第一个线程优先级设置小于第二个线程,还是一直运行void *consumer2(void *p)。
5.如果17, 18行改为
sched.__sched_priority = 0;
pthread_setschedparam(pthread_self(), SCHED_OTHER, &sched);
第二个线程将会变为普通线程
17.初始化互斥锁
切记:互斥锁锁定的是代码块(N行代码区域),而非仅仅锁定一个mutex_t变量或者一个全局变量。举个例子:
91 void *consumer1(void *p)
92 {
93 if(!pthread_mutex_lock(&mu))
94 printf("get mutex(1)\n");
95 else
96 return NULL;
97 sleep(10);
98 pthread_mutex_unlock(&mu);
99 }
100
101 void * consumer2(void *p)
102 {
103 if(!pthread_mutex_lock(&mu))
104 printf("get mutex(2)\n");
105 else
106 return NULL;
107 pthread_mutex_unlock(&mu);
108 }
Int Main(int argc,char * argv[])
{
233 rett = pthread_create(&t1, NULL, consumer1, NULL);
234 rett = pthread_create(&t2, NULL, consumer2,NULL);
235 if(rett != 0)
236 {
237 printf("create failed,%d\n", rett);
238 exit(1);
239 }
240 sleep(2);
241 printf("main returned\n");
242 return 0;
}
运行结果:从结果大家可以看到get mutex(2)根本没有被打印,说明在执行线程A的时候,线程B根本没有被执行。
get mutex (mian)
get mutex (main)
get mutex(1)
main returned
1.动态初始化:
int pthread_mutex_init(pthread_mutex_t *mp,
const pthread_mutexattr_t *mattr);
第一个参数指向互斥锁,第二个参数指向互斥锁的属性对象,将mattr 设置为NULL 的效果与传递缺省互斥锁属性对象的地址相同,但是没有内存开销。
2.静态初始化:
使用PTHREAD_MUTEX_INITIALIZER 宏可以将以静态方式定义的互斥锁初始化为其缺省属性。
返回值
在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EBUSY 描述: 该实现已检测到系统尝试重新初始化mp 所引用的对象,即以前进行过初始化但尚未销毁的互斥锁。
EINVAL 描述: mattr 属性值无效。互斥锁尚未修改。
EFAULT 描述: mp 所指向的互斥锁的地址无效。
注意:如果针对以前初始化的互斥锁调用 pthread_mutex_init(),则该互斥锁还会被重新初始化。
18.销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mp);
此函数没意义,执行之后对锁没有影响,可以不用。
例子:
Pthread_mutex_t mu;
7 void *consumer1(void *p)
8 {
9 pthread_mutex_init(&mu, NULL);
10 pthread_mutex_lock(&mu);
11 printf("get mutex(1)\n");
12 pthread_mutex_unlock(&mu);
13 }
14 void *consumer2(void *p)
15 {
16 pthread_mutex_init(&mu, NULL);
17 pthread_mutex_lock(&mu);
18 printf("get mutex(2)\n");
19 pthread_mutex_unlock(&mu);
20 }
21 int main(int argc, char *argv[])
22 {
23 pthread_t t1, t2, t3;
24 int ret;
25
26 pthread_mutex_init(&mu, NULL);
27 pthread_mutex_lock(&mu);
28 ret = pthread_create(&t1, NULL, consumer1, NULL);
29 ret = pthread_create(&t2, NULL, consumer2,NULL);
30 if(ret != 0)
31 {
32 printf("create failed,%d\n", ret);
33 exit(1);
34 }
35 sleep(11);
36 printf("main returned\n");
37 return 0;
38 }
如果互斥锁在使用前都被初始化,则先前线程锁执行的pthread_mutex_lock就已经失去效果了。
所以运行结果:
get mutex(2)
get mutex(1)
main returned
19.锁定互斥锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
当pthread_mutex_lock() 返回时,该互斥锁已被锁定。调用线程是该互斥锁的属主。如果该互斥锁已被另一个线程锁定和拥有,则调用线程将阻塞,直到该互斥锁变为可用为止。
返回值:
在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EAGAIN 描述: 由于已超出了互斥锁递归锁定的最大次数,因此无法获取该互斥锁。
EDEADLK 描述: 当前线程已经拥有互斥锁
20.解除锁定互斥锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
可释放mutex 引用的互斥锁对象。互斥锁的释放方式取决于互斥锁的类型属性。如果调用pthread_mutex_unlock() 时有多个线程被mutex 对象阻塞,则互斥锁变为可用时调度策略可确定获取该互斥锁的线程。对PTHREAD_MUTEX_ERRORCHECK_NP类型的互斥锁,当计数达到零并且调用线程不再对该互斥锁进行任何锁定时,该互斥锁将变为可用。
返回值
在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
EPERM 描述: 当前线程不拥有互斥锁。
21. 互斥锁的类型:
1.如果互斥锁类型为PTHREAD_MUTEX_TIMED_NP,则不提供死锁检测。尝试重新锁定互斥锁会导致死锁。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或未锁定,则将产生不确定的行为。(默认类型)
2.如果互斥锁类型为PTHREAD_MUTEX_ERRORCHECK_NP,则会提供错误检查。如果某个线程尝试重新锁定的互斥锁已经由该线程锁定,则将返回错误。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或者未锁定,则将返回错误。
3.如果互斥锁类型为PTHREAD_MUTEX_RECURSIVE_NP,则该互斥锁会保留锁定计数这一概念。线程首次成功获取互斥锁时,锁定计数会设置为1。线程每重新锁定该互斥锁一次,锁定计数就增加1。线程每解除锁定该互斥锁一次,锁定计数就减小1。锁定计数达到0 时,该互斥锁即可供其他线程获取。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或者未锁定,则将返回错误。
22. 初始化互斥锁属性对象
int pthread_mutexattr_init(pthread_mutexattr_t *mattr);
pthread_mutexattr_init() 调用会导致分配类型为opaque 的对象。如果未销毁该对象,则会导致内存泄漏。
返回值:pthread_mutexattr_init() 成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
ENOMEM 描述: 内存不足,无法初始化互斥锁属性对象。
23. 销毁互斥锁属性对象
int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr)
可用来取消分配用于维护pthread_mutexattr_init() 所创建的属性对象的存储空间。
24.设置互斥锁类型的属性
int pthread_mutexattr_settype(pthread_mutexattr_t *attr , int type);
type 参数指定互斥锁的类型,有一下类型:
PTHREAD_MUTEX_TIMED_NP 不会检测死锁 0(默认类型)
PTHREAD_MUTEX_ERRORCHECK_NP 提供错误检查 2
PTHREAD_MUTEX_RECURSIVE_NP 递归锁 1
具体的区别看:《21. 互斥锁的类型》
返回值:如果运行成功,pthread_mutexattr_settype 函数会返回零。否则,将返回用于指明错误的错误号。
EINVAL 描