(摘录与APUE)pthread_self函数获取自己的线程ID,而不是通过pid去拿的,如果新线程在主线程调用pthread_create返回之前就运行了,那么在新线程中使用pid将是不正确的ID
#include <stdio.h>
#include <pthread.h>
pthread_t pid = 1;
void *__start_routine(void *nul)
{
printf("%lu\n",(unsigned long)pthread_self());
//be careful of pid
printf("__start_routine() pid_t = %lu\n",(unsigned long)pid);
}
int main(void)
{
pthread_create(&pid,NULL,__start_routine,NULL);
printf("main() pid_t = %lu\n",pid);
printf("Hello World!\n");
pthread_join(pid,NULL);
return 0;
}
pthread_join等待子线程的退出,并拿到子线程退出的返回值
#include <stdio.h>
#include <pthread.h>
/*
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,
(void*)(*start_rtn)(void*),void *arg);
*/
void *_thread_main(void *arg)
{
printf("_thread_main run\n");
pthread_exit(arg);
//return arg;
}
int main(int argc,char *argv[])
{
pthread_t tid;
int idata = 123;
int res = pthread_create(&tid,NULL,_thread_main,(void *)idata);
if(res < 0)
{
printf("pthread_create error!!!\n");
return -1;
}
printf("res = %d\n",res);
void *p = NULL;
pthread_join(tid,&p);
int i = (int)p;
printf("i = %d\n",i);
return 0;
}
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int
makethread(void *(*fn)(void *), void *arg)
{
int err;
pthread_t tid;
pthread_attr_t attr;
err = pthread_attr_init(&attr);
if (err != 0)
return(err);
err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (err == 0)
err = pthread_create(&tid, &attr, fn, arg);
pthread_attr_destroy(&attr);
return(err);
}
pthread_cond_timedwait
1.多个线程操作临界区要使用线程锁
2.当某个线程进入临界区后 该线程发现条件不满足,该线程一直霸占着锁,所以需要该线程主动放弃这个锁,并等待条件达到被唤醒。故出现了 pthread_cond_wait(&g_cond,&g_mutex); 让这个线程锁在某个条件上等待
3.pthread_cond_wait(&g_cond,&g_mutex);背后隐藏的事情
- 拿到锁的线程,把锁暂时丢掉(因为该函数中传入了g_mutex),相当于解锁
- 线程休眠,进行等待
- 线程等待通知,要醒来(重新获取锁)
/usr/lib/x86_64-linux-gnu/libpthread.so 线程库 将上面的三步操作做成了原子操作
总结:pthread_cond_wait 条件等待,我在某个条件要主动让出锁,让其他线程可以获取锁,等待其他线程唤醒我,我再获取锁
pthread_cond_signal(&g_cond)通知条件满足
很多人喜欢在pthread_mutex_lock()和pthread_mutex_unlock()之间调用 pthread_cond_signal或者pthread_cond_broadcast函数,
从逻辑上来说,这种使用方法是完全正确的。但是在多线程 环境中,这种使用方法可能是低效的。
posix1标准说,pthread_cond_signal与pthread_cond_broadcast无需考虑调用线程是否是mutex的拥有者,也就是所,可以在lock与unlock以外的区域调用。
如果我们对调用行为不关心,那么请在lock区域之外调用吧。这里举个例子:
我们假设系统中有线程1和线程2,他们都想获取mutex后处理共享数据,再释放mutex。请看这种序列:
1)线程1获取mutex,在进行数据处理的时候,线程2也想获取mutex,但是此时被线程1所占用,线程2进入休眠,等待mutex被释放。
2)线程1做完数据处理后,调用pthread_cond_signal()唤醒等待队列中某个线程,在本例中也就是线程2。线程1在调用 pthread_mutex_unlock()前,因为系统调度的原因,线程2获取使用CPU的权利,那么它就想要开始处理数据,但是在开始处理之 前,mutex必须被获取,很遗憾,线程1正在使用mutex,所以线程2被迫再次进入休眠。
3)然后就是线程1执行pthread_mutex_unlock()后,线程2方能被再次唤醒
。
从这里看,使用的效率是比较低的,如果再多线程环境中,这种情况频繁发生的话,是一件比较痛苦的事情。
所以觉得,如果程序不关心线程可预知的调度行为,那么最好在锁定区域以外调用他们吧
#include <stdio.h>
#include <pthread.h>
// gcc o test multi_producer_consumer.c -pthread
//定义锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//定义条件
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
#define CUSTOMER_COUNT 2
#define PRODUCER_COUNT 1
int g_count = 0;
void * thread_consumer(void *arg)
{
int inum = (int)arg;
while(1)
{
pthread_mutex_lock(&mutex);
while(g_count == 0)//醒来以后需要重新判断条件是否满足,如果不满足再次等待
{
printf("thread_consumer %d: waiting............\n",inum);
pthread_cond_wait(&cond,&mutex);
printf("thread_consumer %d: wake up............\n",inum);
}
//消费产品
printf("thread_consumer %d: g_count %d\n",inum,g_count);
g_count--;
pthread_mutex_unlock(&mutex);
sleep(1);
}
pthread_exit(0);
}
void * thread_producer(void *arg)
{
int inum = (int)arg;
while(1)
{
pthread_mutex_lock(&mutex);
g_count++;
//生产产品
printf("thread_producer %d :g_count %d\n",inum,g_count);
if(g_count > 0)
{
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex);
sleep(1);
}
pthread_exit(0);
}
int main()
{
int i = 0;
pthread_t tidArray[CUSTOMER_COUNT+PRODUCER_COUNT];
for(i = 0;i < CUSTOMER_COUNT ;i++)
{
pthread_create(&tidArray[i],NULL,thread_consumer,(void *)i);
}
for(i = 0;i < PRODUCER_COUNT ;i++)
{
pthread_create(&tidArray[i+CUSTOMER_COUNT],NULL,thread_producer,(void *)i);
}
for(i = 0;i < CUSTOMER_COUNT + PRODUCER_COUNT ;i++)
{
pthread_join(tidArray[i],NULL);
}
return 0;
}
结果:
thread_consumer 0: waiting............ 消费者0抢到线程,进行条件等待
thread_consumer 1: waiting............ 消费者1抢到线程,进行条件等待
thread_producer 0 :g_count 1 生产者0抢到线程,生产,然后发送信号
thread_consumer 0: wake up............ 消费者0被唤醒
thread_consumer 0: g_count 1 消费者0消费
thread_producer 0 :g_count 1 生产者0抢到线程,生产,然后发送信号
thread_consumer 0: g_count 1 消费者0抢得线程,发现g_count == 1 ,不会进入while(g_count == 0)循环,直接消费掉,没有被正在条件等待的消费者1拿到
thread_consumer 1: wake up............ 消费者1醒来
thread_consumer 1: waiting........... while(g_count == 0)继续进入waiting状态
thread_producer 0 :g_count 1
thread_consumer 0: g_count 1
thread_consumer 1: wake up............
thread_consumer 1: waiting............
thread_producer 0 :g_count 1
thread_consumer 0: g_count 1
thread_consumer 1: wake up............
thread_consumer 1: waiting............
thread_producer 0 :g_count 1 生产者0抢到线程,生产,然后发送信号
thread_consumer 1: wake up........... .消费者1拿到被唤醒
thread_consumer 1: g_count 1 消费者1消费
thread_consumer 0: waiting............ 消费者0进行条件等待
关于:while(g_count == 0)//醒来以后需要重新判断条件是否满足,如果不满足再次等待
Spurious wakeup现象是在条件变量使用中出现的,即一个线程可能即使没有条件变量signal的时候也会被唤醒(a thread might be awoken from its waiting state even though no thread signaled the condition variable.)这样会导致wait线程误以为条件成立,因此,正确做法是通过while判断相应的条件,如下所示:
/* In any waiting thread: */ while(!buf->full) wait(&buf->cond, &buf->lock); /* In any other thread: */ if(buf->n >= buf->size){ buf->full = 1; signal(&buf->cond); }