第十一章 线程:
#include <pthread.h>
int pthread_equal(pthread_t tid1, pthread_t tid2);
if equal return non-zero, else return 0
#include <pthread.h>
pthread_t pthread_self(void);
#include <pthread.h>
int pthread_create(pthread_t * restrict tidp,
const pthread_attr_t * restrict attr,
void * (*start_rtn)(void *), void * restrict arg);
if success return 0, else return error-no, will not modify errno
#include <pthread.h>
void pthread_exit(void * rval_ptr);
#include <pthread.h>
int pthread_join(pthread_t thread, void ** rval_ptr);
if success return 0, else return error-no, will not modify errno
#include <pthread.h>
int pthread_cancel(pthread_t tid);
not wait, just tell the request
if success return 0, else return error-no, will not modify errno
#include <pthread.h>
void pthread_cleanup_push(void (*rtn)(void *), void * arg);
void pthread_cleanup_pop(int execute);
only when thread exit by pthread_exit/pthread_cancel
or pthread_cleanup_pop(non-zero), will call rtn
pthread_cleanup_pop(0) will unregist the last rtn, but not call it
compare:
process thread
fork() pthread_create()
exit() pthread_exit()
waitpid() pthread_join()
atexit() pthread_cleanup_push()
getpid() pthread_self()
abort() pthread_cancel()
#include <pthread.h>
int pthread_detach(pthread_t tid);
if success return 0, else return error-no, will not modify errno
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t * restrict mutex,
const pthread_mutexattr_t * restrict attr);
if success return 0, else return error-no, will not modify errno
int pthread_mutex_destroy(pthread_mutex_t * mutex);
if success return 0, else return error-no, will not modify errno
PTHREAD_MUTEX_INITIALIZER
a mutex/rwlock cannot be lock 2 times in one thread
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t * mutex);
if success return 0, else return error-no, will not modify errno
int pthread_mutex_trylock(pthread_mutex_t * mutex);
if success return 0, else return error-no, will not modify errno
int pthread_mutex_unlock(pthread_mutex_t * mutex);
if success return 0, else return error-no, will not modify errno
lock mutexs need same order, unlock need not
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t * restrict rwlock,
const pthread_rwlockattr_t * restrict attr);
if success return 0, else return error-no, will not modify errno
int pthread_rwlock_destroy(pthread_rwlock_t * rwlock);
if success return 0, else return error-no, will not modify errno
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t * rwlock);
if success return 0, else return error-no, will not modify errno
int pthread_rwlock_wrlock(pthread_rwlock_t * rwlock);
if success return 0, else return error-no, will not modify errno
int pthread_rwlock_unlock(pthread_rwlock_t * rwlock);
if success return 0, else return error-no, will not modify errno
#include <pthread.h>
int pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock);
if success return 0, else return error-no, will not modify errno
int pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock);
if success return 0, else return error-no, will not modify errno
#include <pthread.h>
int pthread_cond_init(pthread_cond_t * restrict cond,
pthread_condattr_t * restrict attr);
if success return 0, else return error-no, will not modify errno
int pthread_cond_destroy(pthread_cond_t * cond);
if success return 0, else return error-no, will not modify errno
PTHREAD_COND_INITIALIZER
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t * restrict cond,
pthread_mutex_t * restrict mutex);
if success return 0, else return error-no, will not modify errno
int pthread_cond_timedwait(pthread_cond_t * restrict cond,
pthread_mutex_t * restrict mutex,
const struct timespec * restrict timeout);
if success return 0, else return error-no, will not modify errno
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanosecods */
};
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t * cond);
if success return 0, else return error-no, will not modify errno
int pthread_cond_broadcast(pthread_cond_t * cond);
if success return 0, else return error-no, will not modify errno
示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void
printids(const char * s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int)pid,
(unsigned int)tid, (unsigned int)tid);
}
void *
thr_fn(void * arg)
{
printids("new thread: ");
return ((void *)0);
}
int
main(void)
{
int err;
pthread_t ntid;
err = pthread_create(&ntid, NULL, thr_fn, NULL);
if (err != 0) {
printf("cannot create thread: %s\n", strerror(err));
exit(1);
}
printids("main thread: ");
sleep(1);
exit(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *
thr_fn1(void * arg)
{
printf("thread 1 returning\n");
return ((void *)1);
}
void *
thr_fn2(void * arg)
{
printf("thread 2 exiting\n");
return ((void *)2);
}
int
main(void)
{
int err;
pthread_t tid1;
pthread_t tid2;
void * tret;
if ((err = pthread_create(&tid1, NULL, thr_fn1, NULL)) != 0) {
printf("cannot create thread 1: %s\n", strerror(err));
exit(1);
}
if ((err = pthread_create(&tid2, NULL, thr_fn2, NULL)) != 0) {
printf("cannot create thread 2: %s\n", strerror(err));
exit(1);
}
if ((err = pthread_join(tid1, &tret)) != 0) {
printf("cannot join with thread 1: %s\n", strerror(err));
exit(1);
}
printf("thread 1 exit code %d\n", (int)tret);
if ((err = pthread_join(tid2, &tret)) != 0) {
printf("cannot join with thread 2: %s\n", strerror(err));
exit(1);
}
printf("thread 2 exit code %d\n", (int)tret);
exit(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void
cleanup(void * arg)
{
printf("cleanup %s\n", (char *)arg);
}
/*
* pop and push implement to be macros
* pop-count must equit to push-count in a func-block
* otherwise, error in complile
*/
void *
thr_fn(void * arg)
{
int idx = (int)arg;
char buff1[64];
char buff2[64]; /* need two buffer! */
printf("thread %d start\n", idx);
sprintf(buff1, "thread %d first handler", idx);
pthread_cleanup_push(cleanup, buff1);
sprintf(buff2, "thread %d second handler", idx);
pthread_cleanup_push(cleanup, buff2);
printf("thread %d push complete\n", idx);
if (idx == 1) {
return (arg);
}
if (idx == 2) {
pthread_exit(arg);
}
pthread_cleanup_pop(0);
if (idx == 3) {
return (arg);
}
if (idx == 4) {
pthread_exit(arg);
}
pthread_cleanup_pop(0);
if (idx == 5) {
return (arg);
}
if (idx == 6) {
pthread_exit(arg);
}
abort();
}
int
main(void)
{
int i;
int err;
pthread_t tid[6];
void * tret;
for (i = 0; i < 6; ++i) {
if ((err = pthread_create(&tid[i], NULL, thr_fn, (void *)(i + 1))) != 0) {
printf("cannot create thread %d: %s\n", i + 1, strerror(err));
exit(1);
}
}
for (i = 0; i < 6; ++i) {
if ((err = pthread_join(tid[i], &tret)) != 0) {
printf("cannot join with thread %d: %s\n", i +1, strerror(err));
exit(1);
}
}
exit(0);
}
#include <stdlib.h>
#include <pthread.h>
struct foo {
int f_count;
pthread_mutex_t f_lock;
/* ... more stuff here ... */
};
struct foo *
foo_alloc(void) /* allocate the object */
{
struct foo * fp;
if ((fp = (struct foo *)malloc(sizeof(struct foo))) != NULL) {
fp->f_count = 1;
if (pthread_mutex_init(&fp->f_lock, NULL) != 0) {
free(fp);
return(NULL);
}
/* ... continue initialization ... */
}
return(fp);
}
void foo_hold(struct foo * fp) /* add a reference to the object */
{
pthread_mutex_lock(&fp->f_lock);
++fp->f_count;
pthread_mutex_unlock(&fp->f_lock);
}
void
foo_rele(struct foo * fp) /* release a reference to the object */
{
pthread_mutex_lock(&fp->f_lock);
if (--fp->f_count == 0) { /* last reference */
pthread_mutex_unlock(&fp->f_lock);
/* if foo_hold here, what can we do?*/
pthread_mutex_destroy(&fp->f_lock);
free(fp);
}
else {
pthread_mutex_unlock(&fp->f_lock);
}
}
#include <stdlib.h>
#include <pthread.h>
#define NHASH 29
#define HASH(fp) (((unsigned int)fp) % NHASH)
struct foo {
int f_count;
pthread_mutex_t f_lock;
struct foo * f_next; /* protected by hashlock */
int f_id;
/* ... more stuff here ... */
};
struct foo * fh[NHASH];
pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;
struct foo *
foo_alloc(void) /* allocate the object */
{
struct foo * fp;
int idx;
if ((fp = (struct foo *)malloc(sizeof(struct foo))) != NULL) {
fp->f_count = 1;
if (pthread_mutex_init(&fp->f_lock, NULL) != 0) {
free(fp);
return(NULL);
}
/* continue initialization */
idx = HASH(fp);
pthread_mutex_lock(&hashlock);
fp->f_next = fh[idx];
fh[idx] = fp;
pthread_mutex_unlock(&hashlock);
}
return(fp);
}
void
foo_hold(struct foo * fp) /* add a reference to the object */
{
pthread_mutex_lock(&fp->f_lock);
++fp->f_count;
pthread_mutex_unlock(&fp->f_lock);
}
struct foo *
foo_find(int id) /* find an existing object */
{
struct foo * fp;
int idx;
pthread_mutex_lock(&hashlock);
for (idx = 0; idx < NHASH; ++idx) {
for (fp = fh[idx]; fp != NULL; fp = fp->f_next) {
if (fp->f_id == id) {
foo_hold(fp);
break;
}
}
if (fp != NULL) {
break;
}
}
pthread_mutex_unlock(&hashlock);
return(fp);
}
void
foo_rele(struct foo * fp) /* release a reference to the object */
{
struct foo * tfp;
int idx;
pthread_mutex_lock(&fp->f_lock);
if (fp->f_count == 1) { /* last reference */
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_lock(&hashlock);
pthread_mutex_lock(&fp->f_lock);
/* need to recheck the condition */
if (--fp->f_count == 0) {
/* remove from list */
idx = HASH(fp);
tfp = fh[idx];
if (tfp == fp) {
fh[idx] = fp->f_next;
}
else {
while (tfp->f_next != fp) {
tfp = tfp->f_next;
}
tfp->f_next = fp->f_next;
}
pthread_mutex_unlock(&hashlock);
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_destroy(&fp->f_lock);
free(fp);
}
else {
pthread_mutex_unlock(&hashlock);
pthread_mutex_unlock(&fp->f_lock);
}
}
else {
--fp->f_count;
pthread_mutex_unlock(&fp->f_lock);
}
}
#include <stdlib.h>
#include <pthread.h>
#define NHASH 29
#define HASH(fp) (((unsigned int)fp) % NHASH)
struct foo {
int f_count; /* protected by hashlock */
pthread_mutex_t f_lock;
struct foo * f_next; /* protected by hashlock */
int f_id;
/* ... more stuff here ... */
};
struct foo * fh[NHASH];
pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;
struct foo *
foo_alloc(void) /* allocate the object */
{
struct foo * fp;
int idx;
if ((fp = (struct foo *)malloc(sizeof(struct foo))) != NULL) {
fp->f_count = 1;
if (pthread_mutex_init(&fp->f_lock, NULL) != 0) {
free(fp);
return(NULL);
}
/* continue initialization */
idx = HASH(fp);
pthread_mutex_lock(&hashlock);
fp->f_next = fh[idx];
fh[idx] = fp;
pthread_mutex_unlock(&hashlock);
}
return(fp);
}
void
foo_hold(struct foo * fp) /* add a reference to the object */
{
pthread_mutex_lock(&hashlock);
++fp->f_count;
pthread_mutex_unlock(&hashlock);
}
struct foo *
foo_find(int id) /* find an existing object */
{
struct foo * fp;
int idx;
pthread_mutex_lock(&hashlock);
for (idx = 0; idx < NHASH; ++idx) {
for (fp = fh[idx]; fp != NULL; fp = fp->f_next) {
if (fp->f_id == id) {
/* do not use foo_hold, will deathlock */
++fp->f_count;
break;
}
}
if (fp != NULL) {
break;
}
}
pthread_mutex_unlock(&hashlock);
return(fp);
}
void
foo_rele(struct foo * fp) /* release a reference to the object */
{
struct foo * tfp;
int idx;
pthread_mutex_lock(&hashlock);
if (--fp->f_count == 0) { /* last reference */
/* remove from list */
idx = HASH(fp);
tfp = fh[idx];
if (tfp == fp) {
fh[idx] = fp->f_next;
}
else {
while (tfp->f_next != fp) {
tfp = tfp->f_next;
}
tfp->f_next = fp->f_next;
}
pthread_mutex_unlock(&hashlock);
pthread_mutex_destroy(&fp->f_lock);
free(fp);
}
else {
pthread_mutex_unlock(&hashlock);
}
}
#include <stdlib.h>
#include <pthread.h>
struct job {
struct job * j_prev;
struct job * j_next;
int j_id; /* tells which thread handles this job */
/* ... more stuff here ... */
};
struct queue {
struct job * q_head;
struct job * q_tail;
pthread_rwlock_t q_lock;
};
/*
* initialize a queue
*/
int
queue_init(struct queue * qp)
{
int err;
if ((err = pthread_rwlock_init(&qp->q_lock, NULL)) != 0) {
return(err);
}
qp->q_head = NULL;
qp->q_tail = NULL;
/* ... continue initialization ... */
return(0);
}
/*
* insert a job at the head of the queue
*/
void
job_insert(struct queue * qp, struct job * jp)
{
pthread_rwlock_wrlock(&qp->q_lock);
jp->j_prev = NULL;
jp->j_next = qp->q_head;
if (qp->q_head != NULL) {
qp->q_head->j_prev = jp;
}
else {
qp->q_tail = jp;
}
qp->q_head = jp;
pthread_rwlock_unlock(&qp->q_lock);
}
/*
* append a job on the tail of the queue
*/
void
job_append(struct queue * qp, struct job * jp)
{
pthread_rwlock_wrlock(&qp->q_lock);
jp->j_next = NULL;
jp->j_prev = qp->q_tail;
if (qp->q_tail != NULL) {
qp->q_tail->j_next = jp;
}
else {
qp->q_head = jp;
}
qp->q_tail = jp;
pthread_rwlock_unlock(&qp->q_lock);
}
/*
* remove the given job from a queue
*/
void
job_remove(struct queue * qp, struct job * jp)
{
pthread_rwlock_wrlock(&qp->q_lock);
if (qp->q_head == jp) {
if (qp->q_tail == jp) {
qp->q_tail = NULL;
}
else {
jp->j_next->j_prev = NULL;
}
qp->q_head = jp->j_next;
}
else if (qp->q_tail == jp) {
jp->j_prev->j_next = NULL;
qp->q_tail = jp->j_prev;
}
else {
jp->j_prev->j_next = jp->j_next;
jp->j_next->j_prev = jp->j_prev;
}
pthread_rwlock_unlock(&qp->q_lock);
}
/*
* find a job for the given thread ID
*/
struct job *
job_find(struct queue * qp, pthread_t id)
{
struct job * jp;
/* read lock must check */
if (pthread_rwlock_rdlock(&qp->q_lock) != 0) {
return(NULL);
}
for (jp = qp->q_head; jp != NULL; jp = jp->j_next) {
if (pthread_equal(jp->j_id, id) != 0) {
break;
}
}
pthread_rwlock_unlock(&qp->q_lock);
return(jp);
}
#include <pthread.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;
while (1) {
pthread_mutex_lock(&qlock);
while (workq == NULL) {
pthread_cond_wait(&qready, &qlock);
}
mp = workq;
workq = workq->m_next;
pthread_mutex_unlock(&qlock);
/* now process the message mp */
}
}
void
enqueue(struct msg * mp)
{
pthread_mutex_lock(&qlock);
mp->m_next = workq;
workq = mp;
pthread_mutex_unlock(&qlock); /* step 1 */
pthread_cond_signal(&qready); /* step 2 */
/*
* we can switch step 1 and step 2,
* they are all right,
* but they have diffect effective
*/
}
#include <sys/time.h>
#include <pthread.h>
void
maketimeout(struct timespec * tsp, long minutes)
{
struct timeval now;
/* get the current time */
gettimeofday(&now, NULL);
tsp->tv_sec = now.tv_sec;
tsp->tv_nsec = now.tv_usec * 1000; /* usec to nsec */
/* add the offset to get timeout value */
tsp->tv_sec += minutes * 60;
}