Linux学习笔记23——pthread_create 创建线程

1,前言

一个进程内可以有多个线程,这些线程可以访问进程的文件描述符、内存。每个线程都有自己的ID,线程ID。线程ID只有在它所属的进程上下午中才有意义。

每个线程含有如下信息:

  1. 线程ID;
  2. 寄存器值;
  3. 栈;
  4. 调度优先级;
  5. 信号屏蔽字
  6. errno
  7. 线程私有数据

一个进程的所有信息都对该进程的所有线程共享,包括:

  1. 可执行程序的代码;
  2. 全局内存;
  3. 堆内存;
  4. 栈;???
  5. 文件描述符

关于进程栈和线程栈总结:

  1.  进程栈大小时执行时确定的,与编译链接无关
  2.  进程栈大小是随机确认的,至少比线程栈要大,但不会超过2倍
  3.  线程栈是固定大小的,可以使用ulimit -a 查看,使用ulimit -s 修改
  4.  一般默认情况下,线程栈是在进程的堆中分配栈空间,每个线程拥有独立的栈空间,为了避免线程之间的栈空间踩踏,线程栈之间还会有以小块guardsize用来隔离保护各自的栈空间,一旦另一个线程踏入到这个隔离区,就会引发段错误。

2,线程ID

线程ID的类型是pthread_t,进程ID类型是pid_t.

pthread_t通常被实现为无符号长整形,但也可能是个结构体。所以判断两个线程ID是否相等,不能直接用等号,必须使用函数:

#include <pthread.h>
int pthread_equal(pthread tid1, pthread tid2);

相等返回非0值,不相等返回0.

获取当前线程的线程ID,则使用下面的函数:

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

3,创建线程

在讲进程的时候我们提到过,可以通过fork和execl把一个可执行文件扔到一个新的进程里去执行。现在,我们想要把某个函数扔到一个新线程里去执行。它需要调用下面的函数:

#include <pthread.h>
int pthread_create(pthread_t *restrict tidp,
                   const pthread_attr_t *restrict attr,
                   void *(*start_rtn)(void*),
                   void *restrict arg);

其中,

  • tidp代表这次创建的新线程的ID;
  • attr代表新线程的属性,暂时不管;
  • start_rtn是我们要扔到新线程里去执行的函数,注意,这个函数的类型被定死了,输入参数是void*,返回类型也是void*;
  • arg是start_rtn的输入参数,如果我们事实上呀传递的参数有多个,那就把这多个参数放到一个结构体里,再把结构体的地址传进来;

4,一个简单的例子

我们写一个简单的例子,它创建新线程,并打印主线程和新线程的ID。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

pthread_t ntid;                //新创建线程的线程ID,线程可以使用进程的全局变量
void  printids(const char* s); //打印当前进程、线程ID
void* thr_fn(void *arg);       //要在线程里执行的函数,输入参数类型和返回类型都是void *

int main(){
	int err;

	//创建新线程,执行thr_fn函数,线程ID存进ntid
	err = pthread_create(&ntid, NULL, thr_fn, NULL);
	if (err)
		err_sys("pthread_creat error!");

	//打印主线程的ID
	printids("main thread");

	//等待1秒,避免新线程运行前进程就退出了
	sleep(1);

	exit(0);
}

//打印当前进程、线程ID
void printids(const char* s)
{
	pid_t     pid; //进程ID
	pthread_t tid; //线程ID

	pid = getpid();
	tid = pthread_self();

	printf("%s: pid %lu, tid %lu (0x%lx)\n", s, (unsigned long)pid, (unsigned long)tid, (unsigned long)tid);
}

//要在线程里执行的函数
void* thr_fn(void *arg)
{
	printids("new thread");
	return((void *)0);
}

代码很简单,但是有两点需要注意:

  1. 我们打印新线程的线程ID,不是在主线程里打印全局变量ntid,而是在新线程里主动获取了ID,为什么要这样呢?因为我们不能保证在调用pthread_creat()后,是新线程先执行,还是主线程先执行。如果在pthread_creat返回后,新线程尚未执行,那么此时全局变量ntid就是未赋值的值。所以安全的做法还是在新线程里获取自己的ID;
  2. 这里主线程休眠了一秒钟。为什么呢?原因同上。如果主线程先执行,可能主线程已经返回了,进程结束了,新线程还没执行呢。所以然主线程休息下,等新线程。
  3. 如果想在新线程里使用ntid(虽然我们这里没用),就必须把他定义成全局变量。因为线程是可以访问进程的全局存储区的。但是,新线程访问不了主线程的栈。所以如果其定义成main函数里的局部变脸,新线程是访问不到的;

这段代码再编译的时候有一点需要注意:如果使用常用的编译指令

g++ -g -W -o study_Linux study_Linux.c 

那么编译会报错:

➜  code gcc -g -W -o study_Linux study_Linux.c
/tmp/cc4sSenu.o: In function `main':
/home/huangyang/code/study_Linux.c:423: undefined reference to `pthread_create'
collect2: error: ld returned 1 exit status

为什么呢?因为pthread的实现并不是在c/c++的标准库里。我们如果想用pthread,就必须自己加上对应的库。所以正确的编译指令是:

g++ -g -W -o study_Linux study_Linux.c -lpthread 

-lpthread 代表pthread的库。

执行后的结果如下:

➜  code ./study_Linux
main thread: pid 140, tid 140676846913344 (0x7ff1e17d0740)
new thread: pid 140, tid 140676836427520 (0x7ff1e0dd0700)

可见,主线程和新线程,进程pid相同,线程pid不同。这个线程pid可能为线程结构的内存地址。

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值