linux多线程编程

linux多线程编程
linux多线程编程

2.终止线程 (2)

3. 等待线程终止 (2)

pthread_exit和pthread_join进一步说明: (3)

4.分离线程 (7)

5.获取线程标识符 (8)

6.比较线程ID (8)

7.一次性初始化 (8)

8. 设置线程的调度策略和优先级 (9)

9. 获取线程的优先级 (11)

10.取消线程 (12)

取消线程,是否会释放线程的所有资源?例子: (14)

设置取消类型 (16)

11.初始化属性 (17)

12.设置分离状态 (18)

13.设置范围 (18)

14. 设置继承的调度策略 (18)

16. 设置调度参数 (19)

17.初始化互斥锁 (21)

18.销毁互斥锁 (21)

19.锁定互斥锁 (22)

20.解除锁定互斥锁 (23)

21. 互斥锁的类型: (23)

22. 初始化互斥锁属性对象 (23)

23. 销毁互斥锁属性对象 (23)

24.设置互斥锁类型的属性 (24)

互斥锁动态初始化和静态初始化区别: (26)

销毁互斥锁:事实上没做任何销毁操作,如下: (27)

非递归类型的互斥锁解锁和加锁操作: (27)

29.初始化条件变量 (27)

30.基于条件变量阻塞 (27)

31.解除阻塞一个线程 (28)

31.解除阻塞所有线程 (29)

33. 在指定的时间之前阻塞 (30)

32.唤醒丢失问题 (31)

33. 计数信号量概述 (31)

34. 初始化信号 (31)

35. 增加信号 (31)

36. 基于信号计数进行阻塞 (32)

37.多线程链表添加删除例子(使用条件变量实现互斥): (32)

38.为线程特定数据创建键 (34)

39. 删除线程特定数据键 (35)

40.设置线程特定数据 (35)

41. 获取线程特定数据 (35)

销毁读写锁属性对象: (36)

设置读写锁共享属性: (37)

初始化读写锁: (37)

销毁读写锁: (37)

读写锁总结: (39)

获得写锁: (39)

获得读锁: (40)

1.创建缺省线程

int pthread_create(pthread_t *tid, cons、劈劈啪啪劈劈啪啪破婆婆t 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()或者设置为分离状态来结束线程,否则线程的资源不会被完全释放。(使用取消线程功能也不能完全释放)

5.主线程运行pthread_exit(),会结束主线程,但不会结束子线程。

6.主线程结束,则整个程序结束,所以主线程最好要使用join等待子线程运行结束。使用join一个线程可以等待多个线程结束。

7.使用join的线程将会阻塞,直到被join的线程结束,join函数返回,但是它对被join的线程运行没有影响。

8.如果子线程使用exit()则可以结束整个进程

例子:

1 #include

2 #include

3 #include

4 #include

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

2 #include

3 #include

4 #include

5 #include

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个左右的线程后,就不能再创建线程,已创建的线程线程号都不同。<<<<<<<<<<<<<<<<<<<<<(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

2 #include

3 #include

4 #include

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

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

2 #include

3 #include

4 #include

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

2 #include

3 #include

4 #include

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("<<<<<<<<<<<<<<<<<<<<

(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)

<<<<<<<<<<<<<<<<<<<<

<<<<<<<<<<<<<<<<<<<<

<<<<<<<<<<<<<<<<<<<<

<<<<<<<<<<<<<<<<<<<<

<<<<<<<<<<<<<<<<<<<<

<<<<<<<<<<<<<<<<<<<<

<<<<<<<<<<<<<<<<<<<<

<<<<<<<<<<<<<<<<<<<<

<<<<<<<<<<<<<<<<<<<<

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<<<<<<<<<<<<<<<<<<<<<<

注意:每次运行的结果都不一样,在主线程里面加了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

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;

17sched.__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 {

相关主题
相关文档
最新文档