之前自己写过一个所谓的线程总结,那是刚学习的时候,想的只是怎么创建线程,后来剖析源码的时候发现了自己在这方面知识的匮乏,于是最近痛定思痛,再去去看了一遍多线程编程。
根据运行环境和调度者身份:
线程可以分为内核线程和用户线程。
内核线程由内核来调度,运行在内核空间
用户线程运行在用户空间由线程库来调度。
当进程的一个内核线程获得CPU使用权时,它就加载并运行一个用户线程。
内核线程相当于用户线程的"容器",一个线程可以拥有M个内核线程和N个用户线程,其中M<=N,并在每个系统中M,N的比值是固定的。
线程实现的三种模式
完全在用户空间实现
完全由内核调度 1:1
双层调度
LinuxThreads和NPTL都是1:1的方式实现的。
现代Linux上默认使用的线程库是NPTL。
#include <pthread.h>
创建一个线程的函数pthread_create:
int pthread_create(pthread_t *thread,const pthread_attr_t* attr,void*(*start_routine)(void*),void* arg);
thread:新线程的标识符,pthread_t是一个整型类型
attr:用于设置新线程的属性,传递NULL则是表示使用默认线程属性
statr_routine:指定新线程将运行的函数
arg:指定新线程的参数
pthread_create成功时返回0,失败时返回错误码。
线程的退出:
#include <pthread.h>
void pthread_exit(void* retval);
线程的回收:
pthread_jion,一个线程中所有的线程都可以调用此函数来回收其他线程(只要他是可回收的),也就是等待其他线程结束,这类似于回收进程的wait和waitpid系统调用。
#include <pthread.h>
int pthread_join(pthread_t thread,void** retval);
thread是目标线程的标识符
retval参数则是目标线程返回的退出信息,成功的话就返回0,失败就返回错误码。
#include <pthread.h>
int pthread_cancel(pthread_t thread);
有时候我们希望异常终止一个线程,也就是取消线程,可以用这个函数来实现。
thread就是目标线程的标识符,该函数成功时返回0,失败则返回错误码。
接收到取消请求的目标线程也可以决定允许被取消还是不允许。
#include <pthread.h>
int pthread_stecancelstate(int state,int* oldstate);//状态
int pthread_stecanceltype(int type,int* oldtype);//类型
14.3线程属性
这个太杂了我的书给的也只是说一下,并不能总结什么,用到的时候查(由于目前还是基础所以没怎么用到这个)
之前的内容我之前的博客也有,就不划重点了,还有就是关于互斥量和条件变量API的总结原先写了。。忘记保存了,所以就不加了。。。具体实现和使用可以直接参考我发上来的代码
POSIX信号量
多线程程序也必须考虑同步问题。
三种专门用于线程同步的机制:
POSIX信号量
互斥量
条件变量
常用的POSIX信号量函数
#include <semaphore.h>
int sem_init(sem_t* sem,int pshared,unsigned int value);
int sem_destroy(sem_t* sem);
int sem_wait(sem_t* sem);//相当于P操作
int sem_trywait(sem_t *sem);
int sem_post(sem_t* sem);//相当于V操作
sem指向被操作的信号量
sem_init函数用于初始化一个未命名的信号量
phared参数指定信号量的类型,如果他的值是0,就表示这个信号量是当前进程的局部信号量,否则该信号量就可以在多个进程之间共享。
value制定的是信号量的初始值(不可以初始化一个已经被初始化了的信号量)
sem_destroy函数用于销毁信号量,释放他占用的内核资源,不能销毁一个被其他线程等待的信号量。
sem_wait函数以原子操作的方式将信号量的值减一,如果信号量值1,那么sem_wait会被阻塞,直到这个信号量具有非0值。
sem_trywait与sem_wait相似,不过它始终立即返回,而不论被操作的信号量是否具有非0值。
sem_post函数以原子操作的方式将信号量加1,当信号量大于0,那么就调用sem_wait等待信号量的线程被唤醒。
他们成功都返回0,失败返回-1
互斥锁用于同步线程对共享数据的访问。
条件变量用于在线程之间同步共享数据的值,条件变量提供了一种线程间的通知机制,当某个共享数据达到某个值的时候唤醒等待这个共享数据的线程。
头文件不想分文档我就直接在头文件定义了
#include <semaphore.h>
#include <pthread.h>
//封装信号量的类
//创建并初始化信号量
//sem,POSIX信号量
int InitSem(sem_t *sem)
{
if(sem_init(sem,0,0)!=0)
{
return -1;//失败就返回-1
}
return 1;//成功返回0
}
int DestroySem(sem_t *sem)
{
if(sem_destroy(sem)!=0)
{
return -1;
}//同上
return 1;
}
int WaitSem(sem_t* sem)//等待,作用相当于PV操作的-1
{
if( sem_wait(sem)!=0)
{
return -1;
}
return 1;
}
int PostSem(sem_t *sem)
{
if(sem_post(sem)!=0)
{
return -1;
}
return 1;
}
//互斥锁
void InitMutex(pthread_mutex_t* mutex)//这个基本和上面一致,想要看是否成功
//就设置返回值0成功,非0失败,我就不写了
{
pthread_mutex_init(mutex,NULL);
}
void DestroyMutex(pthread_mutex_t* mutex)
{
pthread_mutex_destroy(mutex);
}
void LockMutex(pthread_mutex_t *mutex)
{
pthread_mutex_lock(mutex);
}
void UnlockMutex(pthread_mutex_t* mutex)
{
pthread_mutex_unlock(mutex);
}
条件变量
void InitCond(pthread_cond_t* cond)
{
pthread_cond_init(cond,NULL);
}//初始化一个条件变量
void DestroyCond(pthread_cond_t* cond)
{
pthread_cond_destroy(cond);
}
void BroadCond(pthread_cond_t* cond)
{
pthread_cond_broadcast(cond);
}//唤醒所有等待目标条件变量的线程
void SignalCond(pthread_cond_t* cond)//用于唤醒一个等待目标条件变量的线程
//至于那个被唤醒则取决于线程的优先级和调度策略
{
pthread_cond_signal(cond);
}
//等待目标条件变量,mutex函数保证条件变量的互斥锁来保证等待的原子性。
void WaitCond(pthread_cond_t* cond,pthread_mutex_t* mutex)
{
pthread_mutex_lock(mutex);
pthread_cond_wait(cond,mutex);//先解锁,返回再上锁
pthread_mutex_unlock(mutex);
}
POSIX信号量和互斥锁的应用
#include <unistd.h>
//#include <pthread.h>
#include <stdio.h>
#include "lock.h"
//1 sem_t sem1,sem2;
pthread_mutex_t mutex1,mutex2;
void* fun1(void* arg)
{
int i =0;
for(i;i<5;++i)
{
//1 WaitSem(&sem1);//减去1
LockMutex(&mutex1);
printf("启动线程1\n");
UnlockMutex(&mutex2);
//1 PostSem(&sem2);
}
pthread_exit((void*)"thread1 exit\n");
}
void* fun2(void* arg)
{
int i =0;
for(i;i<5;++i)
{
//1 WaitSem(&sem2);//减去1
LockMutex(&mutex2);
printf("启动线程2\n");
UnlockMutex(&mutex1);
//1 PostSem(&sem1);
}
pthread_exit((void*)"thread2 exit\n");
}
int main(int argc,char* argv[])
{
void* ret;
//1 InitSem(&sem1);
//1 InitSem(&sem2);//初始都是0
//1 PostSem(&sem1);
//1 PostSem(&sem2);//都变成1
InitMutex(&mutex1);
InitMutex(&mutex2);
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,fun1,NULL);
pthread_create(&tid2,NULL,fun2,NULL);
pthread_join(tid1,&ret); //ret = "thread1 exit"
pthread_join(tid2,&ret); //ret = "thread2 exit"
//1 DestroySem(&sem1);
//1 DestroySem(&sem2);
DestroyMutex(&mutex1);
DestroyMutex(&mutex2);
return 0;
}
条件变量的应用
#include <unistd.h>
//#include <pthread.h>
#include <stdio.h>
#include "lock.h"
//1 sem_t sem1,sem2;
pthread_mutex_t mutexcond;
pthread_cond_t cond;
void* fun1(void* arg)
{
int i =0;
for(i = 0;i<5;++i)
{
printf("条件改变\n");
SignalCond(&cond);
sleep(1);
}
pthread_exit((void*)"thread1 exit\n");
}
void* fun2(void* arg)
{
int i =0;
for(i;i<5;++i)
{
WaitCond(&cond,&mutexcond);
printf("接收到了条件的改变\n");
}
pthread_exit((void*)"thread2 exit\n");
}
int main(int argc,char* argv[])
{
void* ret;
InitMutex(&mutexcond);
InitCond(&cond);
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,fun1,NULL);
pthread_create(&tid2,NULL,fun2,NULL);
pthread_join(tid1,&ret); //ret = "thread1 exit"
pthread_join(tid2,&ret); //ret = "thread2 exit"
//1 DestroySem(&sem1);
//1 DestroySem(&sem2);
DestroyMutex(&mutexcond);
DestroyCond(&cond);
return 0;
}