#include
int pthread_equal(pthread_t tid1, pthread_t tid2);
pthread_t pthread_self(void);
Unix中进程起始时只有一个master threads,除非使用pthread_create来创建更多的线程。
#include
int phread_create(pthread_t *restrict tidp,
const pthread_attr_t *restrict attr,
void *(*start_rtn)(void *),
void *restrict arg);
//成功返回0,出错返回错误编码
说明:
注意tidp保存的是线程标识符,线程的属性有attr指定,或者用NULL表示使用默认属性,线程创建后从start_rtn开始执行,start_rtn只有一个void *参数,用arg表示。
注意:
线程终止的方式主要有3种:
1.从线程中return
2.线程被其他线程取消
3.线程中调用pthrea_exit
另外需要注意的是,在任意一个进程中的线程里面调用eixt(),_exit()都会造成该进程的终止。
#include
void pthread_exit(void *rval_ptr);
int pthread_join(pthread_t thread, void **rval_ptr);
说明:
pthread_exit与pthrea_join
注意点:
int pthread_cancel(pthread_t tid)
表明我这个线程想要取消tid这个线程。注意只是表示我想要取消,我并不会等到线程真正取消。
void pthread_cleanup_push(void (*rtn)(void *), void *arg);
void pthread_cleanup_pop(int execute);
说明:
作用类似于atexit()
注意:
rtn函数只有在3中情况下会执行:
1.线程调用pthread_exit
2.线程响应别的线程的取消操作
3.使用pthread_cleanup_pop,并且pexecute的参数非0
4.push 与 pop要成对出现
解释:
1.如果execute参数为0,那么就pthread_cleanup_pop函数就什么都不做。
2.如果线程是从主例程中使用return返回,那么就不会执行rtn。主例程指的是创建时使用的start_fun。所以想要执行清理函数要使用pthread_exit。
注意点:
默认情况下,线程的终止状态会保持到我们调用pthread_join。但是如果线程是处于分离状态,那么线程的终止状态会被立即取回。pthread_join并不能作用于一个处于分离状态的线程,这样做会导致pthread_join返回错误。
互斥锁:
互斥锁指的是当我们访问共享内容时,我们先上个锁,在访问结束之后再解开锁。如果我们访问共享内容时,这个共享内容已经被上锁了,我们就会被阻塞起来,直到有个线程释放了这个锁,并且唤醒的是我们。
#include
int pthread_mutex_init(pthread *restrict mutex,
const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(phtread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int phtread_mutex_unlock(pthread_mutex_t *mutex);
互斥锁的操作,注意trylock不会造成阻塞,而lock会造成阻塞。
解决死锁的思路:
1.资源有序加锁
2.使用pthread_mutex_trylock,成功的时候使用lock,不成功就放弃自己已经获得的锁,然后等一会之后重新开始加锁。
类似于lock,但是指定了我们可以锁住的时间是多少,到时间就会返回ETIMEOUT并且释放自己的锁。
说明:
读写锁:
一共有三种状态:
1.写锁状态
2.读锁状态
3.没有锁状态
写锁状态下,要想再加锁就会被阻塞住,无论你是想加读锁,还是想加写锁。在读锁状态下,你可以加读锁,并且不会被阻塞,但是如果你想加写锁,你就会被锁住。
注意如果在读锁模式下,你加了写锁,只能等待前面加上读锁的线程都释放自己的锁之后,才可以加上写锁。但是你表明自己想要加写锁之后,后面的线程想加读锁的话就会被阻塞住。
读写锁适合那些读操作频繁的程序。
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int phtread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
类似于互斥量的try。
注意当锁是可以获得的时候,返回0。否则返回错误代码EBUSY。
#include
int pthread_cond_init(pthread_cond_t *restrict cond,
pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cont_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict timeout);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
说明:
注意点:
注意因为signal与broadcast都有可能唤起多个线程,所有可能一个线程使用了之后,造成条件的再一次不满足,这时就需要再次使用这个函数了。、
例子:
自旋锁会出现忙等,但是不会阻塞。
屏障允许线程等待,知道所有相关的线程到达了同一点,然后线程各自继续执行。
int pthread_barrier_init(pthread_barrier_t *restrict barrier,
const pthread_barrierattr_t *restrict attr,
unsigned int count);
int pthread_barrier_destroy(pthread_barrier_t *barrier);
说明:
count指定了有多少个线程要达到这个屏障,才可以进行下一步工作。
int pthread_barrier_wait(pthread_barrier_t *barrier);
说明:
如果一个线程调用了pthread_barrier_wait之后:
1.如果等待在这个屏障上的线程的个数还没有到达count,那么这个线程会被阻塞。
2.如果在调用之后,count个数到达了,那么所有的线程都会被唤醒。
注意:
线程是一个很重要的概念,他可以提供并发程度。线程可以看成是一个轻量级的进程。在程序开始运行是只有一个主线程,我们可以在线程中创建多个线程,但是注意一个进程可以创建的线程是有上线的。
另外需要注意的线程退出时的一些情况。例如什么时候会执行退出线程清理函数。在线程中调用exit()会怎么样。线程与线程之间的关系,线程与进程之间的关系。
另一部分就是线程同步问题了:主要有互斥量,读写锁,自旋锁,条件变量,屏障。注意死锁问题,已经解决办法。知道同步方法使用的情况有哪些。
另外在这章中看到了指针的另一种用法,注意指针是一个对象,他本身就是相当于一个整形值,我们只是在声明的时候表示这个对象指向的是什么类型。所以可以灵活运用void *。指针之间的比较其实比较的是这两个指针是否指向同一个地址。
POSIX多线程程序设计