【Linux系统编程】线程基本操作
程序员文章站
2022-06-24 17:07:07
...
00. 目录
01. 线程概述
每个进程都拥有自己的数据段、代码段和堆栈段,这就造成进程在进行创建、切换、撤销操作时,需要较大的系统开销。为了减少系统开销,从进程中演化出了线程。为了让进程完成一定的工作,进程必须至少包含一个线程。线程存在于进程中,共享进程的资源。
每个进程都有一个进程号一样,每个线程也有一个线程号。进程号在整个系统中是唯一的,但线程号不同,线程号只在它所属的进程环境中有效。进程号用 pid_t 数据类型表示,是一个非负整数。线程号则用 pthread_t 数据类型来表示,Linux 使用无符号长整数表示。有的系统在实现 pthread_t 的时候,用一个结构体来表示,所以在可移植的操作系统实现不能把它做为整数处理。
02. 线程常用函数
2.1 获取线程号
#include <pthread.h>
pthread_t pthread_self(void);
功能:
获取线程号。
参数:
无
返回值:
调用线程的线程 ID 。
参考代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
//unsigned long
//pthread_t pthread_self(void);
int main(void)
{
//获取当前线程的线程ID
printf("tid: %lu\n", pthread_self());
return 0;
}
2.2 线程号比较
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
功能:
判断线程号 t1 和 t2 是否相等。为了方便移植,尽量使用函数来比较线程 ID。
参数:
t1,t2:待判断的线程号。
返回值:
相等: 非0
不相等:0
参考代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
//unsigned long
//pthread_t pthread_self(void);
int main(void)
{
pthread_t tid = 0;
tid = pthread_self();
//获取当前线程的线程ID
printf("tid: %lu\n", pthread_self());
//比较两个线程ID
printf("equal: %d\n", pthread_equal(tid, pthread_self()));
return 0;
}
注意:
线程函数的程序在 pthread 库中,故链接时要加上参数 -lpthread。
2.3 线程创建
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
Compile and link with -pthread.
功能:
创建一个线程。
参数:
thread:线程标识符地址。
attr:线程属性结构体地址,通常设置为 NULL。
start_routine:线程函数的入口地址。
arg:传给线程函数的参数。
返回值:
成功:0
失败:非 0
pthread_create() 创建的线程从指定的回调函数开始运行,该函数运行完后,该线程也就退出了。线程依赖进程存在的,共享进程的资源,如果创建线程的进程结束了,线程也就结束了。
示例一:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *fun(void *arg)
{
while(1)
{
printf("do work %#x\n", (unsigned long)arg);
sleep(1);
}
return NULL;
}
int main(void)
{
int ret = -1;
pthread_t tid = -1;
//默认的属性
ret = pthread_create(&tid, NULL, fun, (void *)0x88);
if (0 != ret)
{
perror("pthread_create");
goto err0;
}
getchar();
return 0;
err0:
return 1;
}
测试结果:
[email protected]:/mnt/hgfs/LinuxHome/code.bak2/4sys/7th/code$ ./a.out
do work 0x88
do work 0x88
do work 0x88
do work 0x88
do work 0x88
do work 0x88
do work 0x88
do work 0x88
do work 0x88
do work 0x88
do work 0x88
2.4 回收线程资源
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
功能:
等待线程结束(此函数会阻塞),并回收线程资源,类似进程的 wait() 函数。如果线程已经结束,
那么该函数会立即返回。
参数:
thread:被等待的线程号。
retval:用来存储线程退出状态的指针的地址。
返回值:
成功:0
失败:非 0
参考代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *fun(void *arg)
{
printf("do work %#x\n", (unsigned long)arg);
sleep(2);
//线程退出
pthread_exit((void *)0x88);
//return NULL;
}
int main(void)
{
int ret = -1;
void *retval = NULL;
pthread_t tid = -1;
//默认的属性
ret = pthread_create(&tid, NULL, fun, (void *)0x88);
if (0 != ret)
{
perror("pthread_create");
goto err0;
}
printf("main thread do thing1\n");
printf("main thread do thing2\n");
//等待子线程退出 retval
pthread_join(tid, &retval);
printf("retval: %#x\n", retval);
printf("main thread exit..\n");
return 0;
err0:
return 1;
}
执行结果:
[email protected]:/mnt/hgfs/LinuxHome/code.bak2/4sys/7th/code$ ./a.out
main thread do thing1
main thread do thing2
do work 0x88
retval: 0x88
main thread exit..
[email protected]:/mnt/hgfs/LinuxHome/code.bak2/4sys/7th/code$
创建一个线程后应回收其资源,但使用 pthread_join() 函数会使调用者阻塞,Linux 还提供了非阻塞函数 pthread_detach() 来回收线程的资源。
2.5 线程分离
#include <pthread.h>
int pthread_detach(pthread_t thread);
功能:
使调用线程与当前进程分离,分离后不代表此线程不依赖与当前进程,
线程分离的目的是将线程资源的回收工作交由系统自动来完成,也就是说当被分离的线程
结束之后,系统会自动回收它的资源。所以,此函数不会阻塞。
参数:
thread:线程号。
返回值:
成功:0
失败:非 0
测试代码:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void *thead(void *arg)
{
int i;
for(i=0; i<5; i++)
{
printf("I am runing\n");
sleep(1);
}
return NULL;
}
int main(int argc, char *argv[])
{
int ret = 0;
pthread_t tid;
ret = pthread_create(&tid, NULL, thead, NULL);
if(ret!=0){
perror("pthread_create");
}
pthread_detach(tid); // 线程分离,不阻塞
// 立马返回,调用失败
int flag = pthread_join(tid, NULL);
if(flag != 0){
printf("join not working\n");
}
printf("after join\n");
sleep(3);
printf("I am leaving\n");
return 0;
}
测试结果:
[email protected]:/mnt/hgfs/LinuxHome/code.bak2$
[email protected]:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c -pthread
[email protected]:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out
I am runing
join not working
after join
I am runing
I am runing
I am leaving
[email protected]:/mnt/hgfs/LinuxHome/code.bak2$
注意
调用 pthread_detach() 后再调用 pthread_join() , pthread_join() 会立马返回,调用失败。
2.6 线程退出
在进程中我们可以调用 exit() 函数或 _exit() 函数来结束进程,在一个线程中我们可以通过 pthread_exit() 在不终止整个进程的情况下停止它的控制流。
#include <pthread.h>
void pthread_exit(void *retval);
功能:
退出调用线程。一个进程中的多个线程是共享该进程的数据段,因此,
通常线程退出后所占用的资源并不会释放。
参数:
retval:存储线程退出状态的指针。
返回值:
无
测试代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *fun(void *arg)
{
printf("do work %#x\n", (unsigned long)arg);
sleep(5);
//线程退出
pthread_exit(NULL);
//return NULL;
}
int main(void)
{
int ret = -1;
pthread_t tid = -1;
//默认的属性
ret = pthread_create(&tid, NULL, fun, (void *)0x88);
if (0 != ret)
{
perror("pthread_create");
goto err0;
}
printf("main thread do thing1\n");
printf("main thread do thing2\n");
//等待子线程退出
pthread_join(tid, NULL);
printf("main thread exit..\n");
return 0;
err0:
return 1;
}
测试结果:
[email protected]:/mnt/hgfs/LinuxHome/code.bak2/4sys/7th/code$ ./a.out
main thread do thing1
main thread do thing2
do work 0x88
main thread exit..
[email protected]:/mnt/hgfs/LinuxHome/code.bak2/4sys/7th/code$