4.线程的分离属性
通过属性设置线程的分离
1.线程属性类型: pthread_attr_t attr;
2.线程属性操作函数:
(1)对线程属性变量的初始化
int pthread_attr_init(pthread_attr_t* attr);
(2)设置线程分离属性
int pthread_attr_setdetachstate(
pthread_attr_t* attr;
int detachstate;
);
参数:
attr : 线程属性
detachstate:
PTHREAD_CREATE_DETACHED(分离)
PTHREAD_CREATE_JOINABLE(非分离)
(3)释放线程资源函数
int pthread_attr_destroy(pthread_attr_t* attr);
5.线程同步
demo:
两个线程强占交替进行
数据混乱:
1.操作了共享资源
2.CPU调度问题
解决:
线程同步:什么是同步?,协同步调,按照先后顺序操作执行
线程同步的思想:
6.互斥锁(互斥量)
特点:多个线程访问共享数据的时候是串行的
缺点:效率低
1.互斥锁使用的步骤
创建互斥锁: pthread_mutex_t mutex;
初始化:pthread_mutex_init(&mutex,NULL); ——mutex = 1
找到线程共同操作的共享数据:
操作共享资源之前加锁:
pthread_mutex_lock(&mutex); //阻塞 ——mutex = 0
pthread_mutex_trylock(&mutex); // 如果锁上锁直接返回,不阻塞
共享数据操作//临界区 ,越小越好
解锁:pthread_mutex_unlock(&mutex); // ——mutex = 1
阻塞在锁上的线程会被唤醒
销毁:pthread_mutex_destroy(&mutex);
2.互斥锁相关函数
初始化互斥锁:
pthread_mutex_init(
pthread_mutex_t* restrict mutex,
const pthread_mutexattr_t* restrict attr,
);
加锁:
pthread_mutex_lock(pthread_mutex* mutex);
mutex:
没被锁上:当前线程会将这把锁锁上
被锁上了:当前线程阻塞,锁被打开之后,线程解除阻塞
尝试加锁,失败返回,不阻塞:
pthread_mutex_trylock(pthread_mutex_t* mutex);
没有锁上:当前线程会被这把锁加锁
被锁上了:不会阻塞,返回
返回0:加锁 成功 没锁上:返回错误号
if( pthread_mutex_trylock(& mutex)==0)
{
//尝试加锁,并且成功了
//访问共享资源
}
else
{
//错误处理
//或者等待,再次尝试加锁
}
解锁:
pthread_mutex_unlock(pthread_mutex_t* mutex);
销毁互斥锁:
pthread_mutex_destroy(pthread_mutex_t* mutex );
如果我们想要使用互斥锁同步线程所以线程都需要加锁
7.原子操作
CPU处理一个指令,进程/线程在处理完这个指令之前是不会失去CPU的
所谓原子操作,也就是一个独立而不可分割的操作,就像原子被认为是不可分割颗粒一样
更广泛的意义下原子操作是指一系列必须整体完成的操作步骤,如果任何一步操作没有完成,那么所有完成的步骤都必须回滚,这样就可以保证要么所有操作步骤都未完成,要么所有操作步骤都被完成
//示例代码1:
void* procucer(void * arg)
{
while(1)
{
//创建一个链表的节点
Node * newNode = (Node*)malloc(sizeof(Node));
//init
newNode‐>data = rang()%100;
newNode ‐>next = head;
head = newNode;
printf("+++product:%d\n",newNode‐>data);
sleep(rang()%3);
}
reutn NULL;
}
//示例代码2:
void* procucer(void * arg)
{
while(1)
{
//创建一个链表的节点
Node * newNode = (Node*)malloc(sizeof(Node));
//init
newNode‐>data = rang()%100;
//模拟原子操作:
pthread_mutex_lock(&mutex);
newNode ‐>next = head;
head = newNode;
printf("+++product:%d\n",newNode‐>data);
pthread_mutex_unlock(&mutex);
sleep(rang()%3);
}
reutn NULL;
}
8.死锁
造成死锁的原因:
1.自己锁自己
for(int i = 0;i<MAX;i++)
{
pthread_mutex_lock(&mutex);
pthread_mutex_lock(&mutex);
int crt = number;
crt++;
number = crt;
printf("thread A id = %ld,number = %d\n",pthread_self(),number);
pthread_mutex_unlock(&mutex);
usleep(10);
}
2.
线程1:对共享资源A加锁成功-A锁
线程2:对共享资源B加锁成功-B锁
线程1访问共享资源B,对B锁加锁-线程1阻塞在B锁上
线程2访问共享资源A,对A锁加锁-线程2阻塞在A锁上
如何解决:
--让线程按照一定的顺序去访问共享资源
--在访问其他锁的时候,需要先将自己的锁解开
--try_lock
9.读写锁
1.读写锁是几把锁?
一把锁
pthread_rwlock_t lock;
2.读写锁的类型
读锁-对内存做读操作
写锁-对内存做写操作
3.读写锁的特性
线程A加读锁成功,又来了三个线程,做读操作,可以加锁成功
读共享——并行处理
线程A加写锁成功,又来了三个线程,做写操作,三个线程阻塞
写独占——串行处理
线程A加写锁成功,又来了三个线程,做读操作,三个线程阻塞
写独占——写完再读
线程A加读锁成功,又来了三个线程,做写操作,三个线程阻塞
读独占——读完再写
线程A加读锁成功,又来了B线程加写锁阻塞,又来了C线程加读锁阻塞
读写不可以同时进行
写的优先级高
4.读写锁场景练习
1、线程A持有写锁,线程B请求读锁 :线程B阻塞
2、线程A持有读锁,线程B请求写锁 :线程B阻塞
3、线程A持有读锁,线程B请求读锁 :线程B加锁
4、线程A持有读锁,线程B请求写锁,线程C请求读锁 :
1.线程A持有读锁,线程B阻塞,线程C阻塞
2.线程A读完之后,线程B加锁,线程C阻塞
3.线程B写完之后,线程C加锁
5、线程A持有写锁,线程B请求读锁,线程C请求写锁:
1.线程A持有写锁,线程B阻塞,线程C阻塞
2.线程A写完之后,线程C加锁, 线程B阻塞
3.线程C写完之后,线程B加锁
5.读写锁的适用场景
互斥锁:
读写串行
读写锁:
读:并行
写:串行
6.主要操作函数
初始化读写锁:
pthread_rwlock_init(pthread_rwlock_t* restrict rwlock,
const pthread_rwlockattr_t* restrict attr );
加读锁:
pthread_rwlock_rdlock(pthread_rwlock_t* rdlock);
阻塞:之前对这把锁加的是写锁的操作
尝试加读锁:
pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock);
加锁成功:返回0
失败:返回错误号
加写锁:
pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
阻塞:上一次加写锁还没解锁
阻塞:上一次加读锁还没解锁
尝试加写锁:
pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock);
加锁成功:返回0
失败:返回错误号
解锁:
pthread_rwlock_unlock(pthread_rwlock_t* rwlock);
销毁读写锁:
pthread_rwlock_destroy(pthread_rwlock_t* rwlock);
7.练习
三个线程不定时写同一全局变量,五个线程不定时期读同一全局资源
先不加锁:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>int num = 0;void* write_fun(void* arg)
{while(1){num++;printf("write : %ld,%d\n",pthread_self(),num);usleep(500);}
}
void* read_fun(void* arg)
{while(1){printf("read : %ld,%d\n",pthread_self(),num);usleep(500);}
}int main()
{pthread_t p[8];for(int i = 0;i < 3;i++){pthread_create(&p[i],NULL,write_fun,NULL);}for(int i = 3;i < 8;i++){pthread_create(&p[i],NULL,read_fun,NULL);}for(int i = 0;i < 8;i++){pthread_join(p[i],NULL);}return 0;
}
在读的时候会发生混乱
加锁之后:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>int num = 0;
//create rwlock
pthread_rwlock_t lock;void* write_fun(void* arg)
{while(1){//lockpthread_rwlock_wrlock(&lock);num++;printf("write : %ld,%d\n",pthread_self(),num);//unlockpthread_rwlock_unlock(&lock);usleep(500);}
}
void* read_fun(void* arg)
{while(1){//lockpthread_rwlock_rdlock(&lock);printf("read : %ld,%d\n",pthread_self(),num);//unlockpthread_rwlock_unlock(&lock);usleep(500);}
}int main()
{//init rwlockpthread_rwlock_init(&lock,NULL);pthread_t p[8];for(int i = 0;i < 3;i++){pthread_create(&p[i],NULL,write_fun,NULL);}for(int i = 3;i < 8;i++){pthread_create(&p[i],NULL,read_fun,NULL);}for(int i = 0;i < 8;i++){pthread_join(p[i],NULL);}//destroy rwlockpthread_rwlock_destroy(&lock);return 0;
}
这次在读的时候不混乱了