线程

18 篇文章 0 订阅

1. 线程的概念

线程共享以下进程资源和环境:
  • 文件描述符表

  • 每种信号的处理方式(SIG_IGNSIG_DFL或者自定义的信号处理函数)

  • 当前工作目录

  • 用户id和组id

但有些资源是每个线程各有一份的:

  • 线程id

  • 上下文,包括各种寄存器的值、程序计数器和栈指针

  • 栈空间

  • errno变量

  • 信号屏蔽字

  • 调度优先级

2. 线程控制

2.1. 创建线程

#include <pthread.h>

int pthread_create(pthread_t *restrict thread,
	const pthread_attr_t *restrict attr,
	void *(*start_routine)(void*), void *restrict arg);

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

在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给 pthread_create 的函数指针 start_routine 决定。 start_routine 函数接收一个参数,是通过 pthread_create arg 参数传递给它的,该参数的类型为 void * ,这个指针按什么类型解释由调用者自己定义。 start_routine 的返回值类型也是 void * ,这个指针的含义同样由调用者自己定义。 start_routine 返回时,这个线程就退出了,其它线程可以调用 pthread_join 得到 start_routine 的返回值,类似于父进程调用 wait(2) 得到子进程的退出状态

2.2. 终止线程

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

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

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

  • 线程可以调用pthread_exit终止自己。

pthread_cancel终止一个线程分同步和异步两种情况,比较复杂。下面介绍pthread_exit的和pthread_join的用法。

#include <pthread.h>

void pthread_exit(void *value_ptr);

value_ptrvoid *类型,和线程函数返回值的用法一样,其它线程可以调用pthread_join获得这个指针。

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

#include <pthread.h>

int pthread_join(pthread_t thread, void **value_ptr);

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

调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

  • 如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值。

  • 如果thread线程被别的线程调用pthread_cancel异常终止掉,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED

  • 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数

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

3. 线程间同步

3.1. mutex

对于多线程的程序,访问冲突的问题是很普遍的,解决的办法是引入互斥锁(Mutex,Mutual Exclusive Lock) ,获得锁的线程可以完成“ 读-修改-写 ”的操作,然后释放锁给其它线程,没有获得锁的线程只能等待而不能访问共享数据,这样“ 读-修改-写 ”三步操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其它处理器上并行做这个操作。

Mutex用pthread_mutex_t类型的变量表示,可以这样初始化和销毁:

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
       const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

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

pthread_mutex_init函数对Mutex做初始化,参数attr设定Mutex的属性,如果attrNULL则表示缺省属性。用pthread_mutex_init函数初始化的Mutex可以用pthread_mutex_destroy销毁。如果Mutex变量是静态分配的(全局变量或static变量),也可以用宏定义PTHREAD_MUTEX_INITIALIZER来初始化,相当于用pthread_mutex_init初始化并且attr参数为NULL。Mutex的加锁和解锁操作可以用下列函数:

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

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

一个线程可以调用pthread_mutex_lock获得Mutex,如果这时另一个线程已经调用pthread_mutex_lock获得了该Mutex,则当前线程需要挂起等待,直到另一个线程调用pthread_mutex_unlock释放Mutex,当前线程被唤醒,才能获得该Mutex并继续执行。

如果一个线程既想获得锁,又不想挂起等待,可以调用pthread_mutex_trylock,如果Mutex已经被另一个线程获得,这个函数会失败返回EBUSY,而不会使线程挂起等待。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值