第11章 线程

11.3 线程标识

  • 线程ID:只在所属的上下文有效
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
                                return:相等返回非0,否则返回0
  • 获取自身ID
#include <pthread.h>
pthread_t pthread_self(void);
                                return:调用线程的线程ID

11.4 线程的创建

  • 可用pthread_create创建
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
        void *(*start_routine) (void *), void *arg);
  • 参数说明:
    • thread:线程ID
    • attr:线程属性
    • start_routine:新创建线程的起始运行地址
    • arg:需要传递参数的地址
  • 值得注意的:
    • 每个线程提供errno的副本

11.5 线程终止

  • 如果任意线程调用exit..进程就终止
  • 单个线程可以通过下面三种方式退出,可以在不终止整个进程的环境下,停止它的控制流
    • 1.简单从启动例程返回,返回值是线程的退出状态嘛
    • 2.被其他线程取消
    • 3.调用pthread_exit
#include <pthread.h>
void pthread_exit(void *retval);
  • 可以通过调用pthread_join把线程处于分离状态,失败返回EINVAL,
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
  • 可以用下面的函数取消同一进程中的其他线程
#include <pthread.h>
int pthread_cancel(pthread_t thread);

这会使得tid标识的线程表现为如同调用了参数为PTHREAD_CANCELED的pthread_exit()函数
- 线程可以安排退出时需要调用的函数

#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *),void *arg);
void pthread_cleanup_pop(int execute);

线程清理程序放在栈中,就是说执行顺序与注册信息相反,因为可能实现为宏,所以必须以匹配对的方式使用。

  • 从启动例程返回的话,清理程序就不会被执行
  • 可以用下面的函数分离线程
#include <pthread.h>
int pthread_detach(pthread_t thread);

11.6 线程同步

16.1 互斥量

  • 互斥变量是用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);

mutex也可以用PTHREAD_MUTEX_INITIALIZER初始化。
- 有下面三个锁相关的函数

#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);

16.2 避免死锁

  • 如果进程试图对同一个互斥量锁住两次,那么它自身就会陷入死锁状态
  • 需要使用两个互斥量时,总是让以相同的顺序加锁能避免死锁

16.3 函数pthread_mutex_timedlock

#include <pthread.h>
#include <time.h>

int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
        const struct timespec *restrict abs_timeout);
  • 该函数允许绑定线程阻塞时间(绝对时间),
  • 举例说明:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <time.h>
#include <string.h>

int main(){

    int err;
    struct timespec tout;
    struct tm *tmp;
    char buf[64];
    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

    pthread_mutex_lock(&lock);
    printf("mutex is locked\n");
    clock_gettime(CLOCK_REALTIME, &tout);
    tmp = localtime(&tout.tv_sec);

    //"%r":优先用 repr()函数进行字符串转换
    strftime(buf, sizeof(buf), "%r", tmp);
    printf("current time is %s\n", buf);

    tout.tv_sec += 10;  /* 10 seconds from now */
    /* 小心: this could lead to deadlock */
    err = pthread_mutex_timedlock(&lock, &tout);
    clock_gettime(CLOCK_REALTIME, &tout);

    tmp = localtime(&tout.tv_sec);
    strftime(buf, sizeof(buf), "%r", tmp);
    printf("the time is now %s\n", buf);
    if (err == 0)
        printf("mutex locked again!\n");
    else
        printf("can't lock mutex again: %s\n", strerror(err));
    exit(0);
}

11.16.4 读写锁

  • 互斥量要么是锁住状态,要么是不加锁状态,读写锁虽然类似,但是允许更高的并行性。它有三种状态:(也加共享互斥锁(shared-exclusive lock))
    • 读模式下加锁:可以有多个线程占有
    • 写模式下加锁:一次只有一个线程可以占有
    • 不加锁
  • 使用前必须初始化,释放底层内存之前必须销毁
#include <pthread.h>

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
        const pthread_rwlockattr_t *restrict attr);
  • 如果默认属性足够的话可以用PTHREAD_RWLOCK_INITIALIZER初始化
  • 读模式下加锁
#include <pthread.h>

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);//err返回EBUSY
  • 写模式下加锁
#include <pthread.h>

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);//err返回EBUSY
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
  • 解锁
 #include <pthread.h>

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
  • 上面三函数成功都返回0

11.6.5 带有超时的读写锁

 #include <pthread.h>
#include <time.h>

int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock,
        const struct timespec *restrict abs_timeout);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock,
              const struct timespec *restrict abs_timeout);
  • 超时到期时返回 ETIMEDOUT错误

11.6.6 条件变量

  • 使用pthread_cond_t数据类型表示的条件变量可以用两种方式初始化,可以用PTHREAD_COND_INITIALIZER赋给静态分配的条件变量,如果是动态分配的,必须用下面的函数;
#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond,
        const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  • 可以用pthread_cond_timedwait等待条件变为真
#include <pthread.h>

int pthread_cond_timedwait(pthread_cond_t *restrict cond,
        pthread_mutex_t *restrict mutex,
        const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,
        pthread_mutex_t *restrict mutex);
  • abstime指的是绝对时间,表示愿意等待多上时间,如3分钟,就需要把当前时间转化为timespec结构再加三分钟
  • 下面是一个使用条件变量的例子()
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <time.h>
#include <string.h>

struct msg {
    struct msg *m_next;
    /* ... more stuff here ... */
};

struct msg *workq;

/* 下面是一个条件变量,因为是静态分配的内存,所以可以直接使用常量进行初始化 */
pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;

void process_msg(void)
{
    struct msg *mp;

    for (;;) {
        pthread_mutex_lock(&qlock);
        while (workq == NULL)
            pthread_cond_wait(&qready, &qlock);
        mp = workq;
        workq = mp->m_next;
        pthread_mutex_unlock(&qlock);
        /* now process the message mp */
    }
}

void enqueue_msg(struct msg *mp)
{
    pthread_mutex_lock(&qlock);
    mp->m_next = workq;
    workq = mp;
    pthread_mutex_unlock(&qlock);
    pthread_cond_signal(&qready);
}

11.6.7 自旋锁

  • 不使用睡眠使线程阻塞,而是出于忙等,从而避免在调度上话时间
  • 具体看书上335~336

11.6.8 屏障

  • 屏障(barrier)是用户协调多个线程并行工作的同步机制。屏障允许每个线程等待,直到所有的合作线程都达到某一点,然后从该点继续执行。
  • 。。。。

猜你喜欢

转载自blog.csdn.net/qq_36337149/article/details/81275155