上周看了看了进程,这周看了线程,我觉得最能区别两者不同的就是楚东方学长讲firefox是线程管理,chrome是进程管理的,线程是共用地址空间,所以会比进程切换速度更快
一、线程的创建
#include<pthread.h>
int pthread_create(pthread_t *thread,pthrad_attr_t *attr,void*(*start_routine)(void *),void*arg)
参数 | 含义 |
---|---|
thread | 指针,当线程成功创建的时候,返回创建线程ID |
attr | 用于指定线程的属性,NULL为默认属性 |
start_routine | 函数指针,指向线程创建后要调用的函数 |
arg | 该参数指向传递给线程函数的参数 |
线程创建成功,函数返回0,记得gcc的时候加-lpthread
函数 | 含义 |
---|---|
pthread_t pthread_self(void) | 获取本线程的线程ID |
int pthread_equal(pthread_t pthread1,pthread_t pthread2) | 判断两个线程ID是否指向同一个线程,是同一个返回1 |
int pthread_once(pthread_once_t *once_control,void(*init_routine)(void) | 保证init_routine线程函数在进程中仅执行一次 |
arr属性相关函数
//初始化一个pthread_attr类型
int pthread_attr_init(pthread_attr_t *attr);
//销毁一个pthread_attr类型
int pthread_attr_destroy(pthread_attr_t *attr);
//设置/获取一个线程的分离状态,其中detachstate可以设置为:
//①PTHREAD_CREATE_DETACHED 分离状态
//②PTHREAD_CREATE_JOINABLE 正常状态
int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
//设置栈的开始地址
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
//设置/获取线程的栈大小
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
//设置/获取栈警戒缓冲区的大小
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize);
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
int *thread(void *arg)
{
pthread_t newthid;
newthid=pthread_self();
printf("this is a new thread,thread ID=%u\n",newthid);
return NULL;
}
int main()
{
pthread_t thid;
printf("main thread,ID is %u\n",pthread_self());
if(pthread_create(&thid,NULL,(void*)thread,NULL)!=0)
{
printf("thread creation failed\n");
exit(1);
}
sleep(1);
exit(0);
}
pthread_once函数的使用
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
pthread_once_t once=PTHREAD_ONCE_INIT;
void run(void)
{
printf("function run is running in thread %u\n",pthread_self());
}
void *thread1(void *arg)
{
pthread_t thid=pthread_self();
printf("current thread's ID is %u\n",thid);
pthread_once(&once,run);
printf("thread1 ends\n");
}
void *thread2(void *arg)
{
pthread_t thid=pthread_self();
printf("current thread's ID is %u\n",thid);
pthread_once(&once,run);
printf("thread2 ends\n");
}
int main()
{
pthread_t thid1,thid2;
pthread_create(&thid1,NULL,thread1,NULL);
//run函数只会在线程1运行一次
pthread_create(&thid2,NULL,thread2,NULL);
sleep(3);
printf("main thread exit! \n");
}
二、线程的终止
线程终止有两种方法
- 通过return从线程函数返回
- 通过调用pthread_exit( )使线程退出
注意
从main函数里调用exit函数,主进程结束,所有线程结束
调用pthread_exit只会结束单一线程
#include<pthread.h>
void pthread_exit(void *retval);
临界资源被一个线程所使用,当一个线程结束的时候,如果不释放其占有的临界资源,则该资源会被认为还被已经退出的线程所使用,因而永远也不会得到释放,如果另外一个线程等待使用这个临界资源,它就会无限等待,形成死锁
于是linux提供一对函数
void pthread_cleanup_push(void (*routine) (void *), void *arg)
void pthread_cleanup_pop(int execute)
pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理,void routine(void *arg)函数在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push()的调用将在清理函数栈中形成一个函数链,在执行该函数链时按照压栈的相反顺序弹出。execute参数表示执行到pthread_cleanup_pop()时是否在弹出清理函数的同时执行该函数,为0表示不执行,非0为执行;这个参数并不影响异常终止时清理函数的执行。
pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义:
#define pthread_cleanup_push(routine,arg)
{ struct _pthread_cleanup_buffer _buffer;
_pthread_cleanup_push (&_buffer, (routine), (arg));
#define pthread_cleanup_pop(execute)
_pthread_cleanup_pop (&_buffer, (execute)); }
可见,pthread_cleanup_push()带有一个"{",而pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。
三、线程的等待
int pthread_join(pthread_t thread, void **retval);
返回值:若函数执行成功,则返回0,发生错误则会返回错误码。
thread:要获取终止状态的线程ID。
retval:线程的终止状态将写入retval指向的内存区域。
#include<stdio.h>
#include<pthread.h>
void assisthread(void *arg)
{
printf("i am helping to do some jobs\n");
sleep(5);
pthread_exit(0);
}
int main()
{
pthread_t assistthid;
int status;
pthread_create(&assistthid,NULL,(void *)assisthread,NULL);
pthread_join(assistthid,(void*)&status);
printf("assisthread's exit is caused %d\n",status);
return 0;
}
四、私有数据(一键多值)
比如errno是一个全局变量,每个线程可能出错的原因都不一样,所以这时候不能讲errno看做是一个全局变量。这时候就必须用到一键多值
#include<pthread.h>
int pthread_key_creat(pthread_key_t*key,void(*destr_function)(void *));
int pthread_setspecific(pthread_key_t key,const void *pointer);
void *pthread_getspecific(pthread_key_t key);
int pthread_key_delete(pthread_key_t key);
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
pthread_key_t key;
int tsd;
void *thread2(void *arg)
{
tsd=5;
printf("thread2 %d is running\n",pthread_self());
pthread_setspecific(key,(void *)tsd);
printf("thread2 %d returns %d\n",pthread_self(),pthread_getspecific(key));
}
void *thread1(void *arg)
{
tsd=0;
pthread_t thid2;
printf("thread1 %d is running\n",pthread_self());
pthread_setspecific(key,(void *)tsd);
pthread_create(&thid2,NULL,thread2,NULL);
sleep(5);
printf("thread1 %d returns %d\n",pthread_self(),pthread_getspecific(key));
}
int main()
{
pthread_t thid1;
printf("main thread begins running\n");
pthread_key_create(&key,NULL);
pthread_create(&thid1,NULL,thread1,NULL);
sleep(8);//时间尽量长一点,让thread2和thread1都结束才可以
pthread_key_delete(key);
printf("main thread exit\n");
return 0;
}
四、线程同步
1、互斥锁
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const thread_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_unlock(pthread_mutex_t *mutex);
2、条件变量
条件变量是利用线程间共享的全局变量进行同步的一种机制。类似if语句,符合条件就能执行某段代码,否则只能等待
//类似于互斥量、读写锁,静态分配的条件变量可以使用以下两种初始化方式:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//动态分配的条件变量必须使用以下这种初始化方式:
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
//无论静态分配还是动态分配,都应当在不再使用条件变量时销毁它
int pthread_cond_destroy(pthread_cond_t *cond);
返回值:若函数执行成功则返回0,失败则返回表示出错类型的错误码。
cond:要初始化、销毁的条件变量。
attr:新的条件变量的属性,若我们想要以默认的形式创建一个条件变量,则可以将这个属性设置为NULL
//等待条件为真,在此期间会解锁保护条件变量的互斥量
//但当wait返回时会再次锁住保护它的互斥量
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
返回值:若函数执行成功则返回0,反之则返回表示错误类型的错误码。
cond:要等待的条件变量。
mutex:保护条件变量的互斥量。
abstime:在等待条件变量为真之前最长可以等待的时间,这里同互斥量一样,是一个绝对时间。
//至少可以唤醒一个等待该条件的线程
int pthread_cond_signal(pthread_cond_t *cond);
//可以唤醒全部等待该条件的线程
int pthread_cond_broadcast(pthread_cond_t *cond);
返回值:若函数执行成功返回0,反之则返回错误码。
cond:要唤醒的条件。
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
pthread_mutex_t mutex;
pthread_cond_t cond;
void *thread1(void *arg)
{
pthread_cleanup_push(pthread_mutex_unlock,&mutex);
while(1)
{
printf("thread1 is running\n");
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
printf("thread1 applied the condition\n");
pthread_mutex_unlock(&mutex);
sleep(4);
}
pthread_cleanup_pop(0);
}
void *thread2(void *arg)
{
while(1)
{
printf("thread2 is running\n");
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
printf("thread2 applied the condition\n");
pthread_mutex_unlock(&mutex);
sleep(5);
}
}
int main()
{
pthread_t tid1,tid2;
printf("condition variable study!\n");
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
pthread_create(&tid1,NULL,(void *)thread1,NULL);
pthread_create(&tid2,NULL,(void *)thread2,NULL);
do{
pthread_cond_signal(&cond);
}while(1);
sleep(50);
pthread_exit(0);
}