Linux 线程简单介绍

线程

在处理某些问题时,有时候会通过生成子进程来实现,但是,进程是昂贵的,利用子进程来处理相关问题需要很大的开销,因此有时候需要线程来处理,线程可以看成的轻量的进程。

线程数据

同一进程内的线程共享以下数据:

  • 全局内存;
  • 进程指令;
  • 打开的文件;
  • 信号处理函数和信号处置;
  • 当前工作目录;
  • 用户ID和用户组ID;
  • 大多数数据;

每个线程有各自的:

  • 线程ID;
  • 寄存器集合,包括程序计数器和栈指针;
  • 栈;
  • errono;
  • 信号掩码;
  • 优先级;

线程函数

当一个线程以exec执行时,称为主线程或初始线程的线程就已经创建了,其他的线程由函数pthread_create() 创建。

// 返回值:成功则为0,出错为相应的error值;
#include <pthread.h>
int  pthread_create((pthread_t *thread, pthread_attr_t *attr, void  *(*func)(void *), void *arg)
功能:创建线程(实际上就是确定调用该线程函数的入口点),在线程创建以后,就开始运行相关的线程函数。
说明:thread:线程标识符;
     attr:线程属性设置;一般为NULL默认;
     func:线程函数的起始地址;
     arg:传递给func的参数;
返回值:成功,返回0;出错,返回-1;
pthread_t: int or long ;
pthread_attr_t: 属性结构体;

在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached)。一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。默认情况下,线程被创建成可结合的。为了避免存储器泄漏,每个可结合线程都应该要么被显示地回收,即调用pthread_join;要么通过调用pthread_detach函数被分离。

int pthread_join(pthread_t tid, void **status)
功能:等待一个给定线程tid终止;status非空则保存tid返回值
返回值:成功则为0,出错为相应的error值;

int pthread_detach(pthread_t tid);
功能:把指定的线程tid转为脱离状态;tid用于表示线程ID标识,status若非空,则保存所等待线程的返回值;只能等待指定ID的线程,类似于进程中的waitpid函数;
说明:可以pthread_detach(pthread_self())来使自己分离,pthread_self返回本身的线程id;
返回值:成功则为0,出错为相应的error值;

void pthread_exit(void *status);
功能: 退出线程;

int pthread_kill(pthread_t thread, int sig);
功能: 给线程thread发送一个信号:sig;
说明: 接收信号的线程必须先用sigaction函数注册该信号的处理函数;
http://blog.csdn.net/vah101/article/details/40393287

pthread_t pthread_self(void);
函数作用:获得线程自身的ID。pthread_t的类型为unsigned long int,所以在打印的时候要使用%lu方式,否则显示结果出问题。

线程同步

线程中许多数据都是共享的,因此需要对共享数据进行相关的处理,防止线程间对共享数据进行非正常访问和修改。主要的方式有互斥锁、条件变量、信号量等。

互斥锁

互斥锁:访问共享变量前需要先获取互斥锁,用于保护变量,使之在被一个线程使用时不被其它线程访问。
当一个线程获取了互斥锁,那么它将独享这个锁,对共享数据具有操作资格,其它线程在此时无法获取这个互斥锁,也就无法对共享数据进行操作,只有当这个锁被释放,其它线程才能够开始竞争获取这个锁;也就是说在同一时刻,只有一个线程处于执行状态,其它的则在等待获取互斥锁或其它处理过程中。互斥锁用pthread_mutex_t类型的数据变量来表示,使用前必须先进行初始化,可以将之设置为常量PTHREAD_MUTEX_INITIALIZER,也可以通过函数pthread_mutex_init初始化,如果互斥锁是动态分配的,则在释放内存前需要调用pthread_mutex_destroy;

int pthread_mutex_init(pthread_mutex_t *mutex,
                       const pthread_mutexattr_t *attr);  
功能:初始化一个互斥锁mutex;
说明:使用默认属性则attr设为NULL;
返回值:成功返回0,错误相应编号;
pthread_mutex_t: 互斥锁类型结构;
pthread_mutexattr_t: 互斥锁属性结构;

int pthread_mutex_destory(pthread_mutex_t *mutex );  
功能: 丢弃(释放)一个互斥锁mutex;

int pthread_mutex_lock(pthread_mutex_t *mutex);  
int pthread_mutex_trylock(pthread_mutex_t *mutex);  
int pthread_mutex_unlock(pthread_mutex_t *mutex); 
功能: 分别为获取锁,尝试获取锁,解锁 
说明: 参数mutex表示对应的互斥锁指针;第二个函数调用不会阻塞线程,若互斥锁还没有被上锁,那么线程获取这个锁,否则返回EBUSY,线程不会一直阻塞直到获取互斥锁;其它两个函数一直阻塞直到成功返回;
返回值:成功返回0,错误相应编号;

互斥锁的作用其实就是在代码中建立一个临界区域,当线程需要访问一个共享变量时,先获取互斥锁,然后再进行操作,操作完毕之后,再释放该互斥锁;所有的线程在处理共享变量前都要执行相同的操作,这样保证了变量在同一时刻只被一个线程处理。简单示例:

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

#define N 50
int counter;
pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
void *doit(void *arg);

int main(int argc, char **argv)
{
    pthread_t th1, th2;
    pthread_create(&th1, NULL, &doit, NULL);
    pthread_create(&th2, NULL, &doit, NULL);
    pthread_join(th1, NULL);
    pthread_join(th2, NULL);
    exit(0);
}

void *doit(void *arg)
{
    int i;
    for(i = 0; i < N; i++)
    {
        pthread_mutex_lock(&counter_mutex);
        printf("thread: %d    val: %d\n", pthread_self(), counter+1);
        counter += 1;
        pthread_mutex_unlock(&counter_mutex);
    }
    return NULL;
}

可以看到,上面的两个线程在处理共享值counter之前都需要先获取锁。
注意在编译此文件时,需要添加参数-lpthread,原因在于pthread不是linux的默认库,参考pthread_create编译问题

读写锁:读写锁与互斥量类似,但允许更高的并行性,有3种状态:读模式下加锁,写模式下加锁,不加锁,一次只有一个线程可以占有写模式的加锁状态,多个线程可以占有读模式的加锁状态。适合数据结构读的次数远大于写的情况。

自旋锁:与互斥量类似,不通过休眠使线程阻塞,而在在获取锁之前处于忙等(自旋)阻塞状态。可用于:锁被持有的时间短,线程不希望在重新调用上花费太多成本。

条件变量

条件变量是另一种同步机制,通过条件来决定线程是否进程等待。即一个线程执行时需要满足某些条件才执行,但是通过不断的循环来判断条件是否满足这样十分的占资源,可以通过一个条件变量来实现;即建立一个条件变量,当当前线程获取到互斥锁之后,执行到某一步就检查条件变量(此时进程释放锁,其它进程获取该锁并执行操作,当操作完毕,释放锁并返回一个信号,通知前面的线程条件变量满足,之前的线程就再一次获取互斥锁,继续执行后面的操作,实质上是本线程是否继续执行由其它线程来决定),当条件变量满足,继续执行。
条件变量用pthread_cond_t类似的数据表示,通过下面的函数进行初始化和销毁;

int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
功能: 初始化一个条件变量cond;
说明:attr为属性指针,一般使用默认直接设为NULL;可以用常量PTHREAD_COND_INITIALIZER来赋给静态分配的条件变量;
返回值:成功则为0,失败为错误编号;
pthread_cont_t: 条件变量结构
const pthread_condattr_t:条件变量属性结构

int pthread_cond_destory(pthread_cond_t *cond);  
功能: 销毁一个条件变量;

int pthread_cond_signal(pthread_cond_t *cond);  
功能:发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行,如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。
返回值:成功则为0,失败为错误编号;

int pthread_cond_broadcast(pthread_cond_t *cond);  
功能:唤醒所有等待该条件的线程;
返回值:成功则为0,失败为错误编号;

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);  
功能:等待条件变量,过程中释放锁并阻塞;
说明:mutex为当前线程获取的互斥锁指针,函数先对互斥量解锁,当条件变量满足函数返回时,互斥量再次被锁住;
返回值:成功0,失败对应错误码;

int pthread_cond_timedwait(pthread_cond_t *cond,
                           thread_mutex_t *mutex, 
                           const timespec *abstime);  
说明:与第一个功能类似,但设定了条件的等待时间;当时间到了条件还没有满足,那么重新获取互斥锁,返回错误ETIMEDOUT;abstime是一个绝对时间,即当前时间加上等待的时长;pthread_cond_timewait即便是在时间超时的情况下一定会等到真正重新获得mutex之后才会返回。

参考: 在Linux中使用线程

mutex和condition variable的区别是 mutex只能实现互斥访问,但不能对资源计数啊,所以当有多个生产者和消费者时,只能用cond。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值