Linux 线程简析

来源:互联网 发布:flac播放软件 编辑:程序博客网 时间:2024/06/07 17:22

线程概念

  • 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 0