Linux 线程简析

线程概念

  • LWP:light weight process 轻量级的进程,本质仍是进程(在Linux环境下)
  • 进程:独立地址空间,拥有PCB
  • 线程:也有PCB,但没有独立的地址空间(共享)
  • 区别:在于是否共享地址空间。 独居(进程);合租(线程)。
  • Linux下——
    线程:最小的执行单位
    进程:最小分配资源单位,可看成是只有一个线程的进程。
    对于进程来说,相同的地址(同一个虚拟地址)在不同的进程中,反复使用而不冲突。原因是他们虽虚拟址一样,但,页目录、页表、物理页面各不相同。相同的虚拟址,映射到不同的物理页面内存单元,最终访问不同的物理页面。
    但!线程不同!两个线程具有各自独立的PCB,但共享同一个页目录,也就共享同一个页表和物理页面。所以两个PCB共享一个地址空间。
    实际上,无论是创建进程的fork,还是创建线程的pthread_create,底层实现都是调用同一个内核函数clone。
    如果复制对方的地址空间,那么就产出一个“进程”;如果共享对方的地址空间,就产生一个“线程”。
    因此:Linux内核是不区分进程和线程的。只在用户层面上进行区分。所以,线程所有操作函数 pthread_* 是库函数,而非系统调用。

线程共享资源:
  1. 文件描述符表
  2. 每种信号的处理方式
  3. 当前工作目录
  4. 用户ID和组ID
  5. 内存地址空间 (.text/.data/.bss/heap/共享库)
线程非共享资源:
  1. 线程id
  2. 处理器现场和栈指针(内核栈)
  3. 独立的栈空间(用户空间栈)
  4. errno变量
  5. 信号屏蔽字
  6. 调度优先级
线程优缺点:

优点: 1. 提高程序并发性 2. 开销小 3. 数据通信、共享数据方便
缺点: 1. 库函数,不稳定 2. 调试、编写困难、gdb不支持 3. 对信号支持不好


线程函数

pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
  • 参数1:传出参数,保存系统为我们分配好的线程ID
  • 参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
  • 参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。
  • 参数4:线程主函数执行期间所使用的参数。

可以通过调用pthread_self(void)来获取当前线程ID

pthread_exit
void pthread_exit(void *retval);

参数retval传入要返回的数据,通常传NULL。
但要注意不能传在线程栈上分配的内存的数据

pthread_join
int pthread_join(pthread_t thread, void **retval);  //成功0   失败 errno

回收线程
thread线程ID,retval存储线程结束状态
线程通过return或者pthread_exit返回的数据会通过retval接受到,如若对线程终止状态不感兴趣可以传递NULL给retval

pthread_detach
int pthread_detach(pthread_t thread);  //成功 0 失败 errno

指定该状态,线程主动与主控线程断开关系,线程结束后自动释放。
调用该函数将不能通过pthread_join函数获取到线程退出状态,不然将返回EINVAL错误

pthread_cancel
int pthread_cancel(pthread_t thread);  //成功 0 失败 errno

杀死线程,对应进程中的kill函数
该函数不是立即结束线程,而是需要线程达到某个取消点时才能结束

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

void* thread_func(void* ptr)
{
    // 因为这个线程没有cancel point
    while(1)
    {
        // 关闭cancel检测
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

        sleep(10);

        // 打开cancel检测
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

        // 检查cancel point
        pthread_testcancel(); 
    }
    return NULL;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL);

    // 让线程退出
    pthread_cancel(tid);

    // 等待线程退出
    pthread_join(tid, NULL);
}

被取消的线程,退出值定义在Linux的pthread库中。常数PTHREAD_CANCELED的值是-1。可在头文件pthread.h中找到它的定义:#define PTHREAD_CANCELED ((void *) -1)。因此当我们对一个已经被取消的线程使用pthread_join回收时,得到的返回值为-1。

pthread_equal
int pthread_equal(pthread_t t1, pthread_t t2);

比较两个线程ID是否相等


线程属性

对应pthread_create函数的const pthread_attr_t *attr参数

typedef struct

{
int etachstate; //线程的分离状态
int schedpolicy; //线程调度策略
struct sched_param schedparam; //线程的调度参数
int inheritsched; //线程的继承性
int scope; //线程的作用域
size_t guardsize; //线程栈末尾的警戒缓冲区大小
int stackaddr_set; //线程的栈设置
void* stackaddr; //线程栈的位置
size_t stacksize; //线程栈的大小
} pthread_attr_t;

属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。之后须用pthread_attr_destroy函数来释放资源。

线程属性初始化
int pthread_attr_init(pthread_attr_t *attr);   //成功 0 失败 errno //初始化
int pthread_attr_destroy(pthread_attr_t *attr);  //成功 0 失败 errno  //销毁

设置线程属性,分离or非分离

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); 

- 获取程属性,分离or非分离

    int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate); 

参数:

  • attr:已初始化的线程属性
  • detachstate:
    PTHREAD_CREATE_DETACHED(分离线程)
    PTHREAD _CREATE_JOINABLE(非分离线程)

其他属性使用并不常用,暂时还没学习


线程同步

pthread_mutex_init函数
pthread_mutex_destroy函数
pthread_mutex_lock函数
pthread_mutex_trylock函数
pthread_mutex_unlock函数

以上5个函数的返回值都是:成功返回0, 失败返回错误号。    

pthread_mutex_t 类型,其本质是一个结构体。为简化理解,应用时可忽略其实现细节,简单当成整数看待。
pthread_mutex_t mutex; 变量mutex只有两种取值1、0。
pthread_mutex_init
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

初始化一个锁,参数一传入&mutex,参数二,假mutex为全局或者静态的并且用PTHREAD_MUTEX_INITIALIZER进行初始化可以不用init函数,而局部变量则应采用动态初始化,传入NULL

pthread_mutex_destory
int pthread_mutex_destroy(pthread_mutex_t *mutex);  //销毁锁
pthread_mutex_lock
int pthread_mutex_lock(pthread_mutex_t *mutex);  //加锁
pthread_mutex_unlock
int pthread_mutex_unlock(pthread_mutex_t *mutex);  //解锁
pthread_mutex_trylock
int pthread_mutex_trylock(pthread_mutex_t *mutex);  //尝试加锁 加锁失败直接返回错误号,不阻塞

读写锁

pthread_rwlock_init函数
pthread_rwlock_destroy函数
pthread_rwlock_rdlock函数  
pthread_rwlock_wrlock函数
pthread_rwlock_tryrdlock函数
pthread_rwlock_trywrlock函数
pthread_rwlock_unlock函数

以上7 个函数的返回值都是:成功返回0, 失败直接返回错误号。 

pthread_rwlock_t类型  用于定义一个读写锁变量。
pthread_rwlock_t rwlock;
pthread_rwlock_init
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);  //初始化读写锁  attr通常传NULL
pthread_rwlock_destroy
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);  //销毁
pthread_rwlock_rdlock
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);  //以读方式请求读写锁
pthread_rwlock_wrlock
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);  //以写方式请求读写锁
pthread_rwlock_unlock
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);  //解锁
pthread_rwlock_tryrdlock
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);  //非阻塞读方式请求读写锁
pthread_rwlock_trywrlock
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);  //不解释

条件变量

pthread_cond_init函数
pthread_cond_destroy函数
pthread_cond_wait函数
pthread_cond_timedwait函数
pthread_cond_signal函数
pthread_cond_broadcast函数

以上6 个函数的返回值都是:成功返回0, 失败直接返回错误号。
pthread_cond_t类型    用于定义条件变量
pthread_cond_t cond;
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);  //前两个初始化和销毁同上边差不多

int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
//阻塞等待条件变量cond满足并释放已掌握的互斥锁
//当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁

int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
//限时等待一个条件变量
//参看man sem_timedwait函数,查看struct timespec结构体。

int pthread_cond_signal(pthread_cond_t *cond);  //唤醒至少一个阻塞在条件变量上的线程

int pthread_cond_broadcast(pthread_cond_t *cond);  //唤醒全部阻塞在条件变量上的线程

信号量

头文件 <semaphore.h>

sem_init函数
sem_destroy函数
sem_wait函数
sem_trywait函数   
sem_timedwait函数 
sem_post函数

以上6 个函数的返回值都是:成功返回0, 失败返回-1,同时设置errno。(注意,它们没有pthread前缀)

sem_t类型,本质仍是结构体。但应用期间可简单看作为整数,忽略实现细节(类似于使用文件描述符)。 
sem_t sem; 规定信号量sem不能 < 0
int sem_init(sem_t *sem, int pshared, unsigned int value);  
//参1:sem信号量  
//参2:pshared取0用于线程间;取非0(一般为1)用于进程间  
//参3:value指定信号量初值

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
//参2:abs_timeout采用的是绝对时间。
        ↓   
例如定时1秒:
    time_t cur = time(NULL); 获取当前时间。
    struct timespec t;  定义timespec 结构体变量t
    t.tv_sec = cur+1; 定时1秒
    t.tv_nsec = t.tv_sec +100; 
    sem_timedwait(&sem, &t); 传参

互斥量

pthread_mutexattr_t mattr 类型: 用于定义mutex锁的【属性】

int pthread_mutexattr_init(pthread_mutexattr_t *attr); //初始化

int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); //销毁

int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared); //修改

  参2:pshared取值:
      线程锁:PTHREAD_PROCESS_PRIVATE (mutex的默认属性即为线程锁,进程间私有)
      进程锁:PTHREAD_PROCESS_SHARED

文件锁

借助 fcntl函数来实现锁机制。 操作文件的进程没有获得锁时,可以打开,但无法执行read、write操作。

 int fcntl(int fd, int cmd, ... /* arg */ );
  • 参2:
    F_SETLK (struct flock *) 设置文件锁(trylock)
    F_SETLKW (struct flock *) 设置文件锁(lock)W –> wait
    F_GETLK (struct flock *) 获取文件锁

  • 参3:
    struct flock {

    short l_type; 锁的类型:F_RDLCK 、F_WRLCK 、F_UNLCK
    short l_whence; 偏移位置:SEEK_SET、SEEK_CUR、SEEK_END
    off_t l_start; 起始偏移:1000
    off_t l_len; 长度:0表示整个文件加锁
    pid_t l_pid; 持有该锁的进程ID:(F_GETLK only)

    };


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值