线程共享进程的资源(代码段、数据段、堆、核心段),线程也有自己的独立资源:TCB,线程id,寄存器,栈等;
线程分为用户级线程和内核级线程,每个用户级线程都绑定一个内核级线程;
一、多线程的基本函数
头文件:pthread.h
数据类型:pthread_t(typedef unsigned long int pthread_t;)
常用函数:
1. 获取线程自身ID。
pthread_t pthread_self();
2.判断两个线程ID是否相等,返回0 不相等,非零相等。
int pthread_equal(pthread_t threadid1, pthread_t thread2) ;//线程名3.新建线程
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void* (*start_rtn) (void*), void *restrict arg);//参数为线程名指针,属性指针,可为NULL,即为默认属性,线程运行函数指针,线程运行函数的参数指针。
4.线程终止主动退出:不释放资源
return 0;
void pthread_exit(void *rval_ptr);//线程自身主动退出被动退出:
int pthread_cancel(pthread_t tid);//退出tid,不释放资源
int pthread_join(pthread_t tid, void **rval_ptr);//其他线程阻塞自身,等待tid退出,返回值存储在*rval_ptr,可用NULL,不保留tid的返回值,释放资源
5.线程清理
void pthread_cleanup_push(void(*rtn)(void*) , void *arg);
void pthread_cleanup_pop(intexecute);//intexecute非0,线程结束时会自动调用清理函数rtn;6.线程属性//一般不需要专门设置
https://blog.csdn.net/zsf8701/article/details/7842392
数据类型:pthread_attr_t (typedef struct
{
int etachstate; //线程的分离状态,让子线程自动释放资源
structsched_param schedparam; //线程的调度参数
int inheritsched; //线程的继承性
int scope; //线程的作用域
size_t guardsize; //线程栈末尾的警戒缓冲区大小
int stackaddr_set; //线程的栈设置
void* stackaddr; //线程栈的位置
size_t stacksize; //线程栈的大小
}pthread_attr_t;
线程属性的设置要通过函数get\set,先设置属性值,
然后再通过下面两个函数,使用该线程属性
int pthread_attr_init(pthread_attr_t* attr)
int pthread_attr_destroy(pthread_attr_t* attr)
二、线程的互斥(不能并发)
A、互斥锁(还有spinlock,自旋锁,与互斥锁的区别是:自旋锁阻塞时不会让出CPU,会忙等,即在线等,而互斥锁是让出CPU,离线等)
临界资源:共享资源
临界区:对共享资源进行操作的代码段
头文件:pthread.h
数据类型:pthread_mutex_t(一个结构体)
常用函数:
1.互斥锁的创建和销毁:
int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * attr);//属性可初始化为NULL,即为默认
int pthread_mutex_destoryt(pthread_mutex_t * mutex);
2.互斥锁的上锁和解锁:
int pthread_mutex_lock(pthread_mutex_t *mutex) //锁已被占用时,阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex) //锁已被占用时,返回失败
int pthread_mutex_unlock(pthread_mutex_t *mutex) //解锁
3.互斥锁的属性设置//用到了再说
数据类型:pthread_mutexattr_t
设置函数:
int pthread_mutexattr_init(pthread_mutexattr_t* mutexattr)
int pthread_mutexattr_destroy(pthread_mutexattr_t* mutexattr)
互斥锁有四种类型,用到了再补充;
4.互斥锁的弊端:
缺乏多个读线程的并发性,为了解决这个问题,产生了读写锁。
B、读写锁
头文件:pthread.h
数据类型:pthread_rwlock_t(在使用时,报错了没有该类型,解决方法:编译时加上选项:-D_XOPEN_SOURCE=500,在.C文件里加#define _XOPEN_SOURCE 500这个不行)
常用函数:
1.读写锁的创建和销毁:
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);//属性可为NULL,默认值
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);2.加锁和解锁:
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//加读锁,第二次返回阻塞
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);//加读锁,第二次返回失败
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//加写锁,第二次返回阻塞
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);//加写锁,第二次返回失败
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);//解锁互斥情况:多次加读锁,不阻塞或失败;若读锁和写锁并发,返回阻塞或失败
三、线程的同步(在互斥的基础上,安排好线程的先后依赖关系)
C、条件变量(实质是使用一个等待队列和判断条件,该等待队列和判断条件都是共享资源,需要用互斥锁保护,故条件变量需要与互斥锁配合使用,个人觉得这个不是很好用,不太好理解)
头文件:pthread.h
数据类型:pthread_cond_t
常见函数:
1.条件变量的创建和销毁:
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);2.条件变量的等待:
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
3.条件变量的通知:
int pthread_cond_signal(pthread_cond_t *restrict cond);
D、线程信号量(实质是非负整数计数器,是共享资源的数目;当信号量小于0时,阻塞或失败)
头文件:semaphore.h
数据类型:sem_t
常用函数:
1.信号量的创建和销毁:
int sem_init(sem_t *sem, int pshared, unsigned int value);//int pshared为共享属性,一般为0;unsigned int value,信号量的初始值
int sem_destroy(sem_t *sem);
2.信号量加减操作:
int sem_wait(sem_t *sem);//信号量减1,小于0时阻塞
int sem_trywait(sem_t *sem);//信号量减1,小于0时失败
int sem_post(sem_t *sem);//信号量加1
四、死锁
产生死锁的条件:
(1)互斥条件:指进程/线程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程/线程占用。如果此时还有其它进程/线程请求该资源,则请求者只能等待,直至占有该资源的进程/线程用毕释放。
(2)请求和保持条件:指进程/线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其它进程/线程占有,此时请求进程/线程阻塞,但又对自己获得的其它资源保持不放。
(3)不剥夺条件:指进程/线程已获得资源,在使用完之前,不能被剥夺,只能在使用完时由自己释放。
(4)环路等待条件:指在发生死锁时,必然存在一个进程/线程等待资源的环形链,即进程/线程集合(P0,P1,P2,…,Pn)中的P0正在等待一个P1占用的资源;P1正在等待一个P2占用的资源,……,Pn正在等待已被P0占用的资源避免死锁的方法:
1.一次性请求所有临界资源;
2.使用trylock代替lock;
3.各互斥线程采用相同的次序访问共享资源
五、实例
1、互斥锁使用:在一个子线程里写,另一个子线程里读,只保证了子线程是互斥的,并没有实现写与读之间的同步
#include
#include #include #include typedef struct Data { int sum; pthread_mutex_t mlock; }data; void* fn_0(void* arg) { data* da1 = (data*)arg; for (int i = 0; i<10; i++) { pthread_mutex_lock(&da1->mlock); da1->sum += 1; printf("\nwrite data:%d", da1->sum); pthread_mutex_unlock(&da1->mlock); } return (void*)da1; } void* fn_1(void* arg) { data* da2 = (data*)arg; for (int i = 0; i<10; i++) { pthread_mutex_lock(&da2->mlock); printf("\nread data:%d", da2->sum); pthread_mutex_unlock(&da2->mlock); } return (void*)da2; } int main() { pthread_t thread0; pthread_t thread1; data da; da.sum = 0; pthread_mutex_init(&da.mlock, NULL); pthread_create(&thread0, NULL, fn_0, (void*)&da); pthread_create(&thread1, NULL, fn_1, (void*)&da); pthread_join(thread0, NULL); pthread_join(thread1, NULL); pthread_mutex_destroy(&da.mlock); return 0; } 2、信号量使用:在一个子线程里写,另一个子线程里读,实现写与读之间的同步
#include
#include #include #include #include int sum; sem_t sem1; sem_t sem2; void* fn_0(void* arg) { int* sum = (int*)arg; for (int i = 0; i<10; i++) { *sum += 1; printf("write data:%d\n", *sum); sem_post(&sem2); sem_wait(&sem1); } return (void*)arg; } void* fn_1(void* arg) { int* sum = (int*)arg; for (int i = 0; i<10; i++) { sem_wait(&sem2); printf("read data:%d\n", *sum); sem_post(&sem1); } return (void*)arg; } int main() { sum=0; pthread_t thread0; pthread_t thread1; sem_init(&sem1,0,0); sem_init(&sem2,0,0); pthread_create(&thread0, NULL, fn_0, (void*)&sum); pthread_create(&thread1, NULL, fn_1, (void*)&sum); pthread_join(thread0, NULL); pthread_join(thread1, NULL); sem_destroy(&sem1); sem_destroy(&sem2); return 0; } 3.读写锁的使用:
#include
#include #include #include #include typedef struct Data { int sum; int rd_count; int wr_count; pthread_rwlock_t rwlock; }data; time_t start = 0; time_t end = 0; void* fn_0(void* arg) { data* da1 = (data*)arg; int read_sum; start = time(NULL); while(1) { end = time(NULL); if (end == start + 180) break; pthread_rwlock_rdlock(&da1->rwlock); read_sum = da1->sum; da1->rd_count +=1; pthread_rwlock_unlock(&da1->rwlock); } return (void*)da1; } void* fn_1(void* arg) { data* da2 = (data*)arg; while (1) { end = time(NULL); if (end == start + 180) break; pthread_rwlock_wrlock(&da2->rwlock); da2->wr_count +=1; da2->sum += 1; pthread_rwlock_unlock(&da2->rwlock); } return (void*)da2; } int main() { pthread_t thread0; pthread_t thread1; data da; da.sum = 0; da.rd_count = 0; da.wr_count = 0; pthread_rwlock_init(&da.rwlock, NULL); pthread_create(&thread0, NULL, fn_0, (void*)&da); pthread_create(&thread1, NULL, fn_1, (void*)&da); pthread_join(thread0, NULL); pthread_join(thread1, NULL); printf("rd_count,wr_count:%d,%d\n",da.rd_count,da.wr_count); pthread_rwlock_destroy(&da.rwlock); return 0; }