线程

fork 的问题:

  1. 效率不高。内存要从父进程copy到子进程,子进程共享父进程所有的descriptors,等。虽然实现使用了copy-on-write,但仍然是低效的。
  2. 调用fork之后,父子进程间进行信息交换要用到 IPC ( Inter-Process Communication,进程间通信)。在调用fork前,从父进程传递信息到子进程是容易的,因为子进程copy父进程的数据空间和所有的descriptors。但是从子进程向父进程返回信息不方便。

线程可解决上述问题,线程的创建时间比进程快10-100倍。一个进程中所有的线程共享相同的内存空间。这样在线程间共享数据非常容易,但也产生了同步的问题。

线程共享的有:

  1. 全局变量
  2. 进程函数(Process instructions)
  3. 打开的文件
  4. 信号句柄和信号处理函数
  5. 当前工作目录
  6. User ID,group ID

每个线程也有自己的:

  1. 线程ID(类型为 pthread_t 经常为 unsigned int )。在进程中,每一个线程由线程ID唯一标识。
  2. 一组 registers,包括 program counter 和 stack pointer
  3. 堆栈(局部变量和返回地址)
  4. errno
  5. Signal mask
  6. 优先级

线程的建立和终止

#include <pthread.h>

int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func) (void *), void *arg);

返回 0 说明创建成功,返回负值(为 Exxx )说明错误。所有的线程函数都不设置 errno,它们直接返回错误值,这和其它的系统调用(返回-1,设置errno)不同。

线程ID由tid返回。

attr提供线程属性的设置,为空说明使用默认属性。

int pthread_join (pthread_t tid, void ** status);

返回 0 说明成功,返回负值(为 Exxx )说明错误。

等待指定线程结束,类似于waitpid。没有办法等待所有的线程,只能等待指定的线程。如果status不为空,线程的返回值保存到status中。

pthread_t pthread_self (void);

返回线程ID。

int pthread_detach (pthread_t tid);

返回 0 说明成功,返回负值(为 Exxx )说明错误。

一个线程或者是joinable(默认),或者是detached。当joinable线程退出时,它的线程ID和返回值一直保存,直到有其它线程调用pthread_join。但一个detached线程结束后,所有的资源都释放了,它是不能被等待的。如:pthread_detach (pthread_self());

void pthread_exit (void *status);

status不能指向调用线程的局部变量,当线程结束,这些内存会失效。

Thread-Specific Data

系统有两个列表支持Thread-Specific Data。POSIX规定每个进程列表的长度不少于128。一个我们称之为key structures 数组,key structures有如下结构

{

          flag;                   // flag说明这个item是否被使用。默认都是没有使用,当调用pthread_key_create时,找到第一个没有使用的,并返回它的索引(0-127)

          destructor ptr;   // 函数指针,当对应的pkey array中的指针要释放时调用些函数进行释放

}

另一个我们称之为pkey array,是一个128元素的指针。所有的这些指针初始化为0。如果flag表示这一项被使用,则对应的数据在pkey array中。

当线程结束时,pkey array中的指针调用destructor ptr进行清理工作。

int pthread_once(pthread_once_t *onceptr, void (*init) (void));

返回 0 说明成功,返回负值(为 Exxx )说明错误。保证在一个进程中,指定的init函数只运行一次,可以用来做一些初始化的工作。如:

static pthread_key_t rl_key;
static pthread_once_t rl_once = PTHREAD_ONCE_INIT;
static void readline_once(void)
{
    pthread_key_creat(&rl_key, readline_destructor);
}

...
pthread_once(&rl_once, readline_once);
int pthread_key_create(pthread_key_t *keyptr, void (*destructor) (void *value));

返回 0 说明成功,返回负值(为 Exxx )说明错误。

在pkey array中的位置由keyptr返回。destructor设置key structures中的destructor ptr,如果此值不为空,在线程结束时,此函数被调用来对pkey array中对应位置的值进行清理。

void *pthread_getspecific(pthread_key_t key);

指向thread-specific data,可能返回为空。

int pthread_setspecific(pthread_key_t key, const void *value);

返回 0 说明成功,返回负值(为 Exxx )说明错误。设置hread-specific data

int pthread_mutex_lock(pthread_mutex_t * mptr);

int pthread_mutex_unlock(pthread_mutex_t * mptr);

返回 0 说明成功,返回负值(为 Exxx )说明错误。

如果一个mutex变量是静态的,初始化为PTHREAD_MUTEX_INITIALIZER

Condition Variables

在main的主循环中进行sleep,在线程中执行一些操作唤醒主循环,这种情况就要用到 Condition Variables。一个Condition Variables要和mutex变量捆绑使用。

int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr);

int pthread_cond_signal(pthread_cond_t *cptr);

返回 0 说明成功,返回负值(为 Exxx )说明错误。

如果一个pthread_cond_t变量是静态的,初始化为PTHREAD_COND_INITIALIZER。

pthread_mutex_t ndone_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t ndone_cond = PTHREAD_COND_INITIALIZER;

...

pthread_mutex_lock(&ndone_mutex);
...
pthread_cond_signal(&ndone_cond);
pthread_mutex_unlock(&ndone_mutex);

...

pthread_mutex_lock(&ndone_mutex);
...
pthread_cond_wait (&ndone_cond, &ndone_mutex);
...
pthread_mutex_unlock (&ndone_mutex);

pthread_cond_signal 唤醒一个正在等待cptr的线程。当线程调用pthread_cond_wait时,首先解锁,然后sleep,当被唤醒后,再上锁,然后返回。

int pthread_cond_broadcast (pthread_cond_t * cptr);

返回 0 说明成功,返回负值(为 Exxx )说明错误。唤醒所有等待在cptr上的线程。

int pthread_cond_timedwait (pthread_cond_t * cptr, pthread_mutex_t *mptr, const struct timespec *abstime);

返回 0 说明成功,返回负值(为 Exxx )说明错误。加上一个超时的条件。

注意,这里的时间是绝对时间,如2013.2.3,不是15分钟之类的

struct timeval tv;
struct timespec ts;
if (gettimeofday(&tv, NULL) < 0)
{
      err_sys("gettimeofday error");
}

ts.tv_sec = tv.tv_sec + 5; /* 5 seconds in future */
ts.tv_nsec = tv.tv_usec * 1000; /* microsec to nanosec */
pthread_cond_timedwait( ..., &ts);
用绝对时间的好处是当通过信号被唤醒时,时间不会改变

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值