Linux系统应用编程 --- 线程原语

简介: Linux系统应用编程 --- 线程原语

1. pthread_create

1. #include <pthread.h>
2. 
3. int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
4. 
5. 
6. pthread_t *thread:传递一个pthread_t变量地址进来,用于保存新线程的tid(线程ID)
7. 
8. const pthread_attr_t *attr:线程属性设置,如使用默认属性,则传NULL
9. 
10. void *(*start_routine) (void *):函数指针,指向新线程应该加载执行的函数模块
11. 
12. void *arg:指定线程将要加载调用的那个函数的参数

返回值:成功返回0,失败返回错误号。以前学过的系统函数都是成功返回0,失败返回-1,而错误号保存在全局变量errno中,而pthread库的函数都是通过返回值返回错误号,虽然每个线程也都有一个errno,但这是为了兼容其它函数接口而提供的,pthread库本身并不使用它,通过返回值返回错误码更加清晰。

在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。

2. pthread_self

1. #include <pthread.h>
2. 
3. pthread_t pthread_self(void);

获取调用线程tid,其作用对应进程中getpid()函数。

线程ID:pthread_t类型,其本质是在Linux下是无符号整数,其他系统中可能是结构体实现

线程ID是进程内部识别标志。(两个进程间,线程ID允许相同)

注意:不应该使用全局变量pthread_t tid,在子进程中通过pthread_create传出参数来获取线程ID,而应该使用pthread_self()

 

线程ID与线程号的区别:

Ps –eLf命令看到的LWP那一列是线程号,线程号是CPU分配时间轮片的依据

Pthread_self得到的是线程ID,线程ID用来在进程内部区分线程

 

程序1:循坏创建多个子线程

1. #include <stdio.h>
2. #include <string.h>
3. #include <unistd.h>
4. #include <stdlib.h>
5. #include <pthread.h>
6. 
7. void *thread_func(void *arg)
8. {
9.  int i = (int )arg;
10.   sleep(i);
11.   printf("%dth thread: thread id = %lu, pid = %u\n", i+1, pthread_self(), getpid());
12. 
13.   return NULL;
14. }
15. 
16. int  main(void)
17. {
18.   pthread_t tid;
19.   int ret, i;
20. 
21.   for(i=0; i<5; i++){
22.     ret = pthread_create(&tid, NULL, thread_func, (void *)i);
23.     if(ret != 0){
24.       fprintf(stderr, "pthread_create error:%s\n", strerror(ret));
25.       exit(1);
26.     }
27.   }
28. 
29.   //sleep(i);
30. 
31.   //return 0;     //将当前进程退出
32.   pthread_exit(NULL);
33. 
34. }

执行结果如下:

线程默认共享数据段、代码段等地址空间,常用的是全局变量,而进程不共享全局变量,只能借助mmap.

 

程序2:验证线程之间共享全局数据

1. #include <stdio.h>
2. #include <string.h>
3. #include <unistd.h>
4. #include <stdlib.h>
5. #include <pthread.h>
6. 
7. int var = 100;
8. 
9. void *tfn(void *arg)
10. {
11.   var = 200;
12.   printf("thread.\n");
13. 
14.   return NULL;
15. }
16. 
17. int  main(void)
18. {
19.   printf("At first var = %d.\n", var);
20. 
21.   pthread_t tid;
22.   pthread_create(&tid, NULL, tfn, NULL);
23.   sleep(1);
24. 
25.   printf("after pthread_create, var = %d.\n", var);
26. 
27.   return 0;     //将当前进程退出
28. 
29. }

执行结果如下:

主控线程先结束,其他子线程也不会在执行 。如果任意一个线程调用了exit或_exit,则整个进程的所有线程都终止。

3. pthread_exit

1. #include <pthread.h>
2. 
3. void pthread_exit(void *retval);

void *retval:线程退出时传递出的参数,可以是退出值或地址,如是地址时,不能是线程内部申请的局部地址。一般传NULL。

pthread_exit与exit的区别:

调用线程退出函数pthread_exit,只是推出当前线程

任何线程里使用exit都会导致进程退出,主控线程退出时不能return或exit,因为会影响其他线程未工作。

 

exit与_exit的区别:

exit关闭C标准文件流,并刷新文件缓冲区

_exit(Linux底层函数),导致进程退出,关闭未关闭的文件描述符

 

需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

 

4. pthread_join

阻塞等待线程退出,获取线程退出状态,其作用对应进程中的waitpid()函数

1. #include <pthread.h>
2. 
3. int pthread_join(pthread_t thread, void **retval);

pthread_t thread:回收线程的tid

void **retval:接收退出线程传递出的返回值

返回值:成功返回0,失败返回错误号

回收一个线程,获得线程退出值,如果线程没有终止,阻塞等待线程退出

5. pthread_cancel

在进程内的某个线程可以取消另一个线程

1. #include <pthread.h>
2. 
3. int pthread_cancel(pthread_t thread);

 

来一段code熟悉下刚才的API

1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <pthread.h>
4. #include <unistd.h>
5. 
6. void *thr_fn1(void *arg)
7. {
8.  printf("thread 1 returing\n");
9.  return (void *) 1;
10. }
11. 
12. void *thr_fn2(void * arg)
13. {
14.   printf("thread 2 exiting\n");
15.   pthread_exit((void * )2);
16. }
17. 
18. 
19. void *thr_fn3(void * arg)
20. {
21.   while(1)
22.   {
23.     printf("thread3 writing\n");
24.     sleep(1);
25.   }
26. }
27. 
28. int main(void)
29. {
30.   pthread_t tid;
31.   void * tret;
32. 
33.   pthread_create(&tid, NULL, thr_fn1, NULL);
34.   pthread_join(tid, &tret);
35.   printf("thread 1 exit code %d\n", (int )tret);
36. 
37.   pthread_create(&tid, NULL, thr_fn2, NULL);
38.   pthread_join(tid, &tret);
39.   printf("thread 2 exit code %d\n", (int )tret);
40. 
41.   pthread_create(&tid, NULL, thr_fn3, NULL);
42.   sleep(3);
43.   pthread_cancel(tid);
44.   pthread_join(tid, &tret);
45. 
46.   return 0;
47. }

执行结果如下:

 

6. pthread_detach

1. #include <pthread.h>
2. 
3. int pthread_detach(pthread_t tid);

pthread_t tid:分离线程tid

返回值:成功返回0,失败返回错误号。

 

一般情况下,线程终止后,其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL。如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。

7. pthread_equal

比较两个线程是否相等

 

线程终止方式

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

1.从线程主函数return。这种方法对主控线程不适用,从main函数return相当于调用exit。

2.一个线程可以调用pthread_cancel终止同一进程中的另一个线程。

3.线程可以调用pthread_exit终止自己

目录
相关文章
|
1天前
|
SQL 监控 架构师
linux系统性能分析的目的
【4月更文挑战第19天】在Linux系统中,找到性能瓶颈是关键,涉及应用程序、操作系统、硬件和网络的全面排查。优化方案通常针对应用程序和操作系统,而硬件和网络问题较易定位。目标是平衡资源使用,确保系统响应和稳定性。系统管理员、架构设计人员和开发人员共同参与,通过监控硬件、网络、配置和代码来优化性能。流程包括管理员初步判断,架构师处理结构问题,开发人员优化代码,实现系统资源的均衡利用。
6 1
|
3天前
|
Ubuntu Linux
Linux(Ubuntu)系统临时IP以及静态IP配置(关闭、启动网卡等操作)
请注意,以上步骤是在临时基础上进行配置的。如果要永久保存静态IP地址,通常还需要修改 `/etc/network/interfaces`文件,以便在系统重启后保持配置。同时,确保备份相关配置文件以防止出现问题。
16 1
|
4天前
|
Linux 数据安全/隐私保护
Linux系统忘记密码的三种解决办法
这篇博客介绍了三种在Linux忘记密码时重置登录密码的方法:1) 使用恢复模式,通过控制台界面以管理员权限更改密码;2) 利用Linux Live CD/USB启动,挂载硬盘分区并使用终端更改密码;3) 进入单用户模式,自动以管理员身份登录后重置密码。每个方法都提供了详细步骤,提醒用户在操作前备份重要数据。
|
4天前
|
JSON Unix Linux
Linux系统之jq工具的基本使用
Linux系统之jq工具的基本使用
32 2
|
19天前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
30天前
|
存储 缓存 NoSQL
Redis单线程已经很快了6.0引入多线程
Redis单线程已经很快了6.0引入多线程
31 3
|
1月前
|
消息中间件 安全 Linux
线程同步与IPC:单进程多线程环境下的选择与权衡
线程同步与IPC:单进程多线程环境下的选择与权衡
58 0
|
1月前
|
Java 调度 C#
C#学习系列相关之多线程(一)----常用多线程方法总结
C#学习系列相关之多线程(一)----常用多线程方法总结
|
1月前
|
安全 编译器 C#
C#学习相关系列之多线程---lock线程锁的用法
C#学习相关系列之多线程---lock线程锁的用法
|
1月前
|
Java C#
C#学习系列相关之多线程(五)----线程池ThreadPool用法
C#学习系列相关之多线程(五)----线程池ThreadPool用法