1.什么是线程?
linux内核中是没有线程这个概念的,而是轻量级进程的概念:LWP。一般我们所说的线程概念是C库当中的概念。
1.1线程是怎样描述的?
线程实际上也是一个task_struct,工作线程拷贝主线程的task_struct,然后共用主线程的mm_struct。线程ID是在用task_struct中pid描述的,而task_struct中tgid是线程组ID,表示线程属于该线程组,对于主线程而言,其pid和tgid是相同的,我们一般看到的进程ID就是tgid。
即:

获取线程ID和主线程ID的值:

#include <sys/syscall.h>
int TID = syscall(SYS_gettid);



通过group_leader指针, 每个线程都能找到主线程。主线程存在一个链表头,后面创建的每一个线程都会链入到该双向链表中。
2.线程创建


#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
void *ThreadWork(void *arg)
{
int *p = (int*)arg;
printf("i am work thread:%p, data:%dn",pthread_self(),*p);
pthread_exit(NULL);
}
int main()
{
int i = 1;
pthread_t tid;
int ret = pthread_create(&tid,NULL,ThreadWork,(void*)&i);//不要传临时变量,这里是示范
if(ret != 0)
{
perror("pthread_create");
return -1;
}
while(1)
{
printf("i am main work threadn");
sleep(1);
}
return 0;
}
#include <pthread.h>
pthread_t pthread_self(void);
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);



#include <pthread.h>
int pthread_attr_setstacksize(pthread_attr_t *attr,size_t stacksize);
int pthread_attr_getstacksize(pthread_attr_t *attr,size_t *stacksize);
3.线程终止
#include <pthread.h>
void pthread_exit(void *retval);
参数:retval是返回信息,”临终遗言“,可以给可以不给
该变量不能使用临时变量。
可使用:全局变量、堆上开辟的空间、字符串常量。
4.线程等待
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);


#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
void *ThreadWork(void *arg)
{
int *p = (int*)arg;
printf("pid : %dn",syscall(SYS_gettid));
printf("i am work thread:%p, data:%dn",pthread_self(),*p);
sleep(3);
pthread_exit(NULL);
}
int main()
{
int i = 1;
pthread_t tid;
int ret = pthread_create(&tid,NULL,ThreadWork,(void*)&i);//不要传临时变量,这里是示范
if(ret != 0)
{
perror("pthread_create");
return -1;
}
pthread_join(tid,NULL);//线程等待
while(1)
{
printf("i am main work threadn");
sleep(1);
}
return 0;
}
5.线程分离
接口:#include <pthread.h>
int pthread_detach(pthread_t thread);

6.线程安全

#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#define NUMBER 4
int g_val = 100;
pthread_mutex_t mutex;//定义互斥锁
void *ThreadWork(void *arg)
{
int *p = (int*)arg;
pthread_detach(pthread_self());//自己分离自己,不用主线程回收它的资源了
while(1)
{
pthread_mutex_lock(&mutex);//加锁
if(g_val > 0)
{
printf("i am pid : %d,i get g_val : %dn",(int)syscall(SYS_gettid),g_val);
--g_val;
usleep(2);
}
else{
pthread_mutex_unlock(&mutex);//在所有可能退出的地方,进行解锁
break;
}
pthread_mutex_unlock(&mutex);//解锁
}
pthread_exit(NULL);
}
int main()
{
pthread_t tid[NUMBER];
pthread_mutex_init(&mutex,NULL);//互斥锁初始化
int i = 0;
for(;i < NUMBER;++i)
{
int ret = pthread_create(&tid[i],NULL,ThreadWork,(void*)&g_val);//不要传临时变量,这里是示范
if(ret != 0)
{
perror("pthread_create");
return -1;
}
}
//pthread_join(tid,NULL);//线程等待
//pthread_detach(tid);//线程分离
pthread_mutex_destroy(&mutex);//销毁互斥锁
while(1)
{
printf("i am main work threadn");
sleep(1);
}
return 0;
}

int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,const struct timespec *restrict abs_timeout);
查看多个线程堆栈:thread apply all bt
跳转到线程中:t 线程号
查看具体的调用堆栈:f 堆栈号
直接从pid号用gdb调试:gdb attach pid
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#define NUMBER 2
pthread_mutex_t mutex1;//定义互斥锁
pthread_mutex_t mutex2;
void *ThreadWork1(void *arg)
{
int *p = (int*)arg;
pthread_mutex_lock(&mutex1);
sleep(2);
pthread_mutex_lock(&mutex2);
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
return NULL;
}
void *ThreadWork2(void *arg)
{
int *p = (int*)arg;
pthread_mutex_lock(&mutex2);
sleep(2);
pthread_mutex_lock(&mutex1);
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);
return NULL;
}
int main()
{
pthread_t tid[NUMBER];
pthread_mutex_init(&mutex1,NULL);//互斥锁初始化
pthread_mutex_init(&mutex2,NULL);//互斥锁初始化
int i = 0;
int ret = pthread_create(&tid[0],NULL,ThreadWork1,(void*)&i);
if(ret != 0)
{
perror("pthread_create");
return -1;
}
ret = pthread_create(&tid[1],NULL,ThreadWork2,(void*)&i);
if(ret != 0)
{
perror("pthread_create");
return -1;
}
//pthread_join(tid,NULL);//线程等待
//pthread_join(tid,NULL);//线程等待
//pthread_detach(tid);//线程分离
pthread_join(tid[0],NULL);
pthread_join(tid[1],NULL);
pthread_mutex_destroy(&mutex1);//销毁互斥锁
pthread_mutex_destroy(&mutex2);//销毁互斥锁
while(1)
{
printf("i am main work threadn");
sleep(1);
}
return 0;
}






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


#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); //阻塞类型的读加锁接口
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); //非阻塞类型的读加锁接口
#include <pthread.h>
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);// 非阻塞写
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//阻塞写
#include <pthread.h>
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
PTHREAD_RWLOCK_PREFER_READER_NP, //读者优先
PTHREAD_RWLOCK_PREFER_WRITER_NP, //很唬人, 但是也是读者优先
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP, //写者优先
PTHREAD_RWLOCK_DEFAULT_NP = PTHREAD_RWLOCK_PREFER_READER_NP

1. 无线程持有写锁,即_writer = 0.
2. 采用读者优先策略或者当前没有写锁申请请求,即 _nr_writers_queue = 0
3. 当满足这两个条件时,读锁请求立即获得读锁,返回之前执行_nr_readers++,表示多了一个线程正在读
4. 不满足这两个条件时,执行_nr_readers_queued++,表示增加了一个读锁等待者,然后调用futex,陷入阻塞。醒来之后,执行_nr_readers_queued- -,再次判断是否满足条件1,2
1. 无线程持有写锁,即_writer = 0.
2. 没有线程持有读锁,即_nr_readers = 0.
3. 如果上述条件满足,就会立即拿到锁,将_writer 置为当前线程的ID
4. 如果不满足,则执行_nr_writers_queue++, 表示增加了一个写锁等待者线程,然后执行futex陷入等待。醒来后,先执行_nr_writers_queue- -,再继续判断条件1,2
1. 执行_writer = 0.,表示释放写锁。
2. 根据_nr_writers_queue判断有没有写锁,如果有则唤醒一个写锁,如果没有写锁等待者,则唤醒所有的读锁等待者。
1. 执行_nr_readers- -,表示读锁占有者少了一个。
2. 判断_nr_readers是否等于0,是的话则表示当前线程是最后一个读锁占有者,需要唤醒写锁等待者或读锁等待者
3. 根据_nr_writers_queue判断是否存在写锁等待者,若有,则唤醒一个写锁等待线程
4. 如果没有写锁等待者,判断是否存在读锁等待者,若有,则唤醒全部的读锁等待者
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <fcntl.h>
#define THREADCOUNT 100
static int count = 0;
static pthread_rwlock_t lock;
void* Read(void* i)
{
while(1)
{
pthread_rwlock_rdlock(&lock);
printf("i am 读线程 : %d, 现在的count是%dn", (int)syscall(SYS_gettid), count);
pthread_rwlock_unlock(&lock);
//sleep(1);
}
}
void* Write(void* i)
{
while(1)
{
pthread_rwlock_wrlock(&lock);
++count;
printf("i am 写线程 : %d, 现在的count是: %dn", (int)syscall(SYS_gettid), count);
pthread_rwlock_unlock(&lock);
sleep(1);
}
}
int main()
{
//close(1);
//int fd = open("./dup2_result.txt", O_CREAT | O_RDWR);
//dup2(fd, 1);
pthread_t tid[THREADCOUNT];
pthread_rwlock_init(&lock, NULL);
for(int i = 0; i < THREADCOUNT; ++i)
{
if(i % 2 == 0)
{
pthread_create(&tid[i], NULL, Read, (void*)&i);
}
else
{
pthread_create(&tid[i], NULL, Write, (void*)&i);
}
}
for(int i = 0; i < THREADCOUNT; ++i)
{
pthread_join(tid[i], NULL);
}
pthread_rwlock_destroy(&lock);
return 0;
}
7.线程间同步
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t *attr);

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict conpthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
条件不会无缘无故地突然变得满足了, 必然会牵扯到共享数据的变化。所以一定要有互斥锁来保护。没有互斥锁, 就无法安全地获取和修改共享数据。
同步并没有保证互斥,而保证互斥是使用到了互斥锁。
pthread_mutex_lock(&m)
while(condition_is_false)
{
pthread_mutex_unlock(&m);
//解锁之后, 等待之前, 可能条件已经满足, 信号已经发出, 但是该信号可能会被错过
cond_wait(&cv);
pthread_mutex_lock(&m);
}
上面的解锁和等待不是原子操作。解锁以后, 调用cond_wait之前,如果已经有其他线程获取到了互斥量, 并且满足了条件, 同时发出了通知信号, 那么cond_wait将错过这个信号, 可能会导致线程永远处于阻塞状态。所以解锁加等待必须是一个原子性的操作, 以确保已经注册到事件的等待队列之前, 不会有其他线程可以获得互斥量。
那先注册等待事件, 后释放锁不行吗?注意, 条件等待是个阻塞型的接口, 不单单是注册在事件的等待队列上, 线程也会因此阻塞于此, 从而导致互斥量无法释放, 其他线程获取不到互斥量, 也就无法通过改变共享数据使等待的条件得到满足, 因此这就造成了死锁。
pthread_mutex_lock(&m);
while(condition_is_false)
pthread_cond_wait(&v,&m);//此处会阻塞
/*如果代码运行到此处, 则表示我们等待的条件已经满足了,
*并且在此持有了互斥量
*/
/*在满足条件的情况下, 做你想做的事情。
*/
pthread_mutex_unlock(&m);
pthread_cond_wait内部会进行解锁逻辑,则一定要先放到PCB等待序列中,再进行解锁。
while(condition_is_false)
pthread_cond_wait(&v,&m);//此处会阻塞
if(condition_is_false)
pthread_cond_wait(&v,&m);//此处会阻塞
唤醒以后, 再次检查条件是否满足, 是不是多此一举?
因为唤醒中存在虚假唤醒(spurious wakeup) , 换言之,条件尚未满足, pthread_cond_wait就返了。在一些实现中, 即使没有其他线程向条件变量发送信号, 等待此条件变量的线程也有可能会醒来。
条件满足了发送信号, 但等到调用pthread_cond_wait的线程得到CPU资源时, 条件又再次不满足了。好在无论是哪种情况, 醒来之后再次测试条件是否满足就可以解决虚假等待的问题。
pthread_cond_wait内部实现逻辑:
- 将调用pthread_cond_wait函数的执行流放入到PCB等待队列当中
- 解锁
- 等待被唤醒
- 被唤醒之后:
1、从PCB等待队列中移除出来
2、抢占互斥锁
情况1:拿到互斥锁,pthread_cond_wait就返回了
情况2:没有拿到互斥锁,阻塞在pthread_cond_wait内部抢锁的逻辑中
当阻塞在pthread_cond_wait函数抢锁逻辑中时,一旦执行流时间耗尽,意味着线程就被切换出来了,程序计数器就保存的是抢锁的指令,上下文信息保存的就是寄存器的值
当再次拥有CPU资源后,恢复抢锁逻辑
直到抢锁成功,pthread_cond_wait函数才会返回
7.3.3条件变量的唤醒
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
pthread_cond_signal负责唤醒等待在条件变量上的一个线程。
pthread_cond_broadcast,就是广播唤醒等待在条件变量上的所有线程。
先发送信号,然后解锁互斥量,这个顺序是必须的嘛?
7.3.4条件变量的销毁
int pthread_cond_destroy(pthread_cond_t *cond);
注意:
1、永远不要用一个条件变量对另一个条件变量赋值, 即pthread_cond_t cond_b = cond_a不合法, 这种行为是未定义的。
2、使用PTHREAD_COND_INITIALIZE静态初始化的条件变量, 不需要被销毁。
3、要调用pthread_cond_destroy销毁的条件变量可以调用pthread_cond_init重新进行初始化。
4、不要引用已经销毁的条件变量, 这种行为是未定义的。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#define NUMBER 2
int g_bowl = 0;
pthread_mutex_t mutex;//定义互斥锁
pthread_cond_t cond1;//条件变量
pthread_cond_t cond2;//条件变量
void *WorkProduct(void *arg)
{
int *p = (int*)arg;
while(1)
{
pthread_mutex_lock(&mutex);
while(*p > 0)
{
pthread_cond_wait(&cond2,&mutex);//条件等待,条件不满足,陷入阻塞
}
++(*p);
printf("i am workproduct :%d,i product %dn",(int)syscall(SYS_gettid),*p);
pthread_cond_signal(&cond1);//通知消费者
pthread_mutex_unlock(&mutex);//释放锁
}
return NULL;
}
void *WorkConsume(void *arg)
{
int *p = (int*)arg;
while(1)
{
pthread_mutex_lock(&mutex);
while(*p <= 0)
{
pthread_cond_wait(&cond1,&mutex);//条件等待,条件不满足,陷入阻塞
}
printf("i am workconsume :%d,i consume %dn",(int)syscall(SYS_gettid),*p);
--(*p);
pthread_cond_signal(&cond2);//通知生产者
pthread_mutex_unlock(&mutex);//释放锁
}
return NULL;
}
int main()
{
pthread_t cons[NUMBER],prod[NUMBER];
pthread_mutex_init(&mutex,NULL);//互斥锁初始化
pthread_cond_init(&cond1,NULL);//条件变量初始化
pthread_cond_init(&cond2,NULL);//条件变量初始化
int i = 0;
for(;i < NUMBER;++i)
{
int ret = pthread_create(&prod[i],NULL,WorkProduct,(void*)&g_bowl);
if(ret != 0)
{
perror("pthread_create");
return -1;
}
ret = pthread_create(&cons[i],NULL,WorkConsume,(void*)&g_bowl);
if(ret != 0)
{
perror("pthread_create");
return -1;
}
}
for(i = 0;i < NUMBER;++i)
{
pthread_join(cons[i],NULL);//线程等待
pthread_join(prod[i],NULL);
}
pthread_mutex_destroy(&mutex);//销毁互斥锁
pthread_cond_destroy(&cond1);
pthread_cond_destroy(&cond2);
while(1)
{
printf("i am main work threadn");
sleep(1);
}
return 0;
}





8.线程取消
int pthread_cancel(pthread_t thread);

void pthread_cleanup_push(void (*routine)(void *),void *arg);
void pthread_cleanup_pop(int execute);
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#define NUMBER 2
int g_bowl = 0;
pthread_mutex_t mutex;//定义互斥锁
void clean(void *arg)
{
printf("Clean up:%sn",(char*)arg);
pthread_mutex_unlock(&mutex);//释放锁
}
void *WorkCancel(void *arg)
{
pthread_mutex_lock(&mutex);
pthread_cleanup_push(clean,"clean up handler");//清除函数的push
struct timespec t = {3,0};//取消点
nanosleep(&t,0);
pthread_cleanup_pop(0);//清除
pthread_mutex_unlock(&mutex);
}
void *WorkWhile(void *arg)
{
sleep(5);
pthread_mutex_lock(&mutex);
printf("i get the mutexn");//若能拿到资源,则表示取消清理函数成功!
pthread_mutex_unlock(&mutex);
return NULL;
}
int main()
{
pthread_t cons,prod;
pthread_mutex_init(&mutex,NULL);//互斥锁初始化
int ret = pthread_create(&prod,NULL,WorkCancel,(void*)&g_bowl);//该线程拿到锁,然后挂掉
if(ret != 0)
{
perror("pthread_create");
return -1;
}
int ret1 = pthread_create(&cons,NULL,WorkWhile,(void*)&ret);//测试该线程是否可以拿到锁
if(ret1 != 0)
{
perror("pthread_create");
return -1;
}
pthread_cancel(prod);//取消该线程
pthread_join(prod,NULL);//线程等待
pthread_join(cons,NULL);//线程等待
pthread_mutex_destroy(&mutex);//销毁互斥锁
while(1)
{
sleep(1);
}
return 0;
}

9.多线程与fork()
10.生产者与消费者模型
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <queue>
#include <sys/syscall.h>
#define PTHREAD_COUNT 2
int data = 0;//全局变量作为插入数据
pthread_mutex_t mutex1;
class ModelOfConProd{
public:
ModelOfConProd()//构造
{
_capacity = 10;
pthread_mutex_init(&_mutex,NULL);
pthread_cond_init(&_cons,NULL);
pthread_cond_init(&_prod,NULL);
}
~ModelOfConProd()//析构
{
_capacity = 0;
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cons);
pthread_cond_destroy(&_prod);
}
void Push(int data)//push数据,生产者线程使用的
{
pthread_mutex_lock(&_mutex);
while((int)_queue.size() >= _capacity)
{
pthread_cond_wait(&_prod,&_mutex);
}
_queue.push(data);
pthread_mutex_unlock(&_mutex);
pthread_cond_signal(&_cons);
}
void Pop(int& data)//pop数据,消费者线程使用的
{
pthread_mutex_lock(&_mutex);
while(_queue.empty())
{
pthread_cond_wait(&_cons,&_mutex);
}
data = _queue.front();
_queue.pop();
pthread_mutex_unlock(&_mutex);
pthread_cond_signal(&_prod);
}
private:
int _capacity;//容量大小,限制容量大小
std::queue<int> _queue;//队列
pthread_mutex_t _mutex;//互斥锁
pthread_cond_t _cons;//消费者条件变量
pthread_cond_t _prod;//生产者条件变量
};
void *ConsumerStart(void *arg)//消费者入口函数
{
ModelOfConProd *cp = (ModelOfConProd *)arg;
while(1)
{
cp->Push(data);
printf("i am pid : %d,i push :%dn",(int)syscall(SYS_gettid),data);
pthread_mutex_lock(&mutex1);//++的时候,给该全局变量加锁
++data;
pthread_mutex_unlock(&mutex1);
}
}
void *ProductsStart(void *arg)//生产者入口函数
{
ModelOfConProd *cp = (ModelOfConProd *)arg;
int data = 0;
while(1)
{
cp->Pop(data);
printf("i am pid : %d,i pop :%dn",(int)syscall(SYS_gettid),data);
}
}
int main()
{
ModelOfConProd *cp = new ModelOfConProd;
pthread_mutex_init(&mutex1,NULL);
pthread_t cons[PTHREAD_COUNT],prod[PTHREAD_COUNT];
for(int i = 0;i < PTHREAD_COUNT; ++i)
{
int ret = pthread_create(&cons[i],NULL,ConsumerStart,(void*)cp);
if(ret < 0)
{
perror("pthread_create");
return -1;
}
ret = pthread_create(&prod[i],NULL,ProductsStart,(void*)cp);
if(ret < 0)
{
perror("pthread_create");
return -1;
}
}
for(int i = 0;i < PTHREAD_COUNT;++i)
{
pthread_join(cons[i],NULL);
pthread_join(prod[i],NULL);
}
pthread_mutex_destroy(&mutex1);
return 0;
}
287
