一、互斥锁

机制:一次只能一个线程拥有互斥锁,其他线程只有等待。

互斥锁是在抢锁失败的情况下主动放弃CPU,进入睡眠状态直到锁的状态改变时再唤醒,而操作系统负责线程调度,为了实现锁的状态发生改变时能唤醒阻塞的线程或者进程,需要把锁交给操作系统管理,所以互斥锁在加锁操作时涉及上下文的切换。

互斥锁实际的效率还是可以让人接受的,加锁的时间大概100ns左右,而实际上互斥锁的一种可能的实现是先自旋一段时间,当自旋的时间超过阀值之后再将线程投入睡眠中,因此在并发运算中使用互斥锁(每次占用锁的时间很短)的效果可能不亚于使用自旋锁。

互斥锁属于sleep-waiting类型的锁。例如在一个双核的机器上有两个线程A和B,它们分别运行在core 0和core 1上。假设线程A想要通过pthread_mutex_lock操作去得到一个临界区的锁,而此时这个锁正被线程B所持有,那么线程A就会被阻塞,此时会通过上下文切换将线程A置于等待队列中,此时core 0就可以运行其他的任务(如线程C)。

互斥锁存在优先唤醒的问题。

接口:

1、初始化锁

int pthread_mutex_init(pthread_mutex_t* mutex,const pthread_mutexattr_t* attr);

参数1:锁的地址
参数2:设置锁的属性,NULL默认缺省属性

互斥锁的属性在创建锁的时候指定,当资源被某线程锁住的时候,其它的线程在试图加锁时表现将不同。当前有四个值可供选择:

1)PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。

2)PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。

3)PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。

4)PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,等待解锁后重新竞争。

2、阻塞加锁
如果是锁是空闲状态,本线程将获得这个锁;如果锁已经被占据,本线程将排队等待,直到成功的获取锁。

int pthread_mutex_lock(pthread_mutex *mutex);

3、非阻塞加锁
如果是锁是空闲状态,本线程将获得这个锁,若锁已经被占据时立即返回 EBUSY,不是挂起等待。

int pthread_mutex_trylock( pthread_mutex_t *mutex);

4、解锁
线程把自己持有的锁释放。

int pthread_mutex_unlock(pthread_mutex *mutex);

4、销毁锁,释放资源(此时锁必需unlock状态,否则返回EBUSY)

int pthread_mutex_destroy(pthread_mutex *mutex);

用法1:使用互斥量(锁)解决多线程抢占资源的问题

/* ************************************************************************
> File Name:     pthread_mutex.cpp
> Author:        想名字多费事
> 微信公众号:     还没弄好
> Created Time:  2021年04月24日 星期六 18时09分54秒
> Description:   ************************************************************************/
//使用互斥量(锁)解决多线程抢占资源的问题
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<unistd.h>
#include<pthread.h>
#include<cstring>
using namespace std;char buffer[1024];//全局共享的buffer//声明互斥锁方式1
pthread_mutex_t mutex;void *pthfun(void *arg){for(int ii=0;ii<2;ii++){cout<<time(0)<<":"<<(long)arg<<">>lock..."<<endl;//第一步,加锁pthread_mutex_lock(&mutex);cout<<time(0)<<":"<<(long)arg<<">>lock ok! 正在处理数据..."<<endl;//第二步,操作共享的全局变量sprintf(buffer,"%d:%ld,%d",time(0),pthread_self(),ii);sleep(5);//第三步,解锁pthread_mutex_unlock(&mutex);cout<<time(0)<<":"<<(long)arg<<">>unlock...ok!"<<endl;usleep(100);//互斥锁存在优先唤醒的问题}
}int main()
{   //初始化锁pthread_mutex_init(&mutex,NULL);//NULL表示当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁//创建两个线程pthread_t pthid1,pthid2;pthread_create(&pthid1,NULL,pthfun,(void*)1);pthread_create(&pthid2,NULL,pthfun,(void*)2);//等待线程退出pthread_join(pthid1,NULL);pthread_join(pthid2,NULL);//销毁锁pthread_mutex_destroy(&mutex);return 0;
}

运行结果

1619265078:2>>lock...
1619265078:2>>lock ok! 正在处理数据...
1619265078:1>>lock...
1619265083:2>>unlock...ok!
1619265083:1>>lock ok! 正在处理数据...
1619265083:2>>lock...
1619265088:1>>unlock...ok!
1619265088:2>>lock ok! 正在处理数据...
1619265088:1>>lock...
1619265093:2>>unlock...ok!
1619265093:1>>lock ok! 正在处理数据...
1619265098:1>>unlock...ok!

用法2:使用互斥量(锁)实现数据库连接池的服务端程序
(此程序涉及数据库环境设置和部分框架的加入,不一定会运行成功,感兴趣可到github里面下载完整代码https://github.com/GpsLypy)

/**  编译指令:g++ -g -Wno-write-strings  -o serverdb serverdb.cpp -I/oracle/home/rdbms/public  -L/oracle/home/lib -L. -lclntsh  /freecplus/db/oracle/_ooci.cpp /freecplus/_freecplus.cpp -lpthread -lm -lc
*/
#include "/freecplus/_freecplus.h"
#include "/freecplus/db/oracle/_ooci.h"pthread_mutex_t mutexs[100];  // 用于数据库连接池的锁。connection conns[100];  // 数据库连接池。bool initconns();  // 初始化数据库连接池。connection *getconn(); // 从连接池中获取一个数据库连接。void freeconn(connection *in_conn); // 释放数据库连接。void freeconns(); // 释放数据库连接池。void *pthmain(void *arg);CTcpServer TcpServer;   // 创建服务端对象。vector<long> vpthid;    // 存放线程id的容器。void mainexit(int sig);  // 信号2和15的处理函数。// 线程清理函数。void pthmainexit(void *arg);CLogFile logfile;int main(int argc,char *argv[]){signal(2,mainexit);  signal(15,mainexit);  // 捕获信号2和15logfile.Open("/tmp/serverdb.log","a+");if (TcpServer.InitServer(5858)==false) {// 初始化TcpServer的通信端口。logfile.Write("TcpServer.InitServer(5858) failed.\n"); return -1;}if (initconns()==false){  // 初始化数据库连接池。logfile.Write("initconns() failed.\n"); return -1;}while (true){if (TcpServer.Accept()==false){   // 等待客户端连接。 logfile.Write("TcpServer.Accept() failed.\n"); return -1;}logfile.Write("客户端(%s)已连接。\n",TcpServer.GetIP());pthread_t pthid;if (pthread_create(&pthid,NULL,pthmain,(void *)(long)TcpServer.m_connfd)!=0){logfile.Write("pthread_create failed.\n"); return -1; }vpthid.push_back(pthid);   // 把线程id保存到vpthid容器中。}return 0;}void *pthmain(void *arg){pthread_cleanup_push(pthmainexit,arg);  // 设置线程清理函数。pthread_detach(pthread_self());  // 分离线程。pthread_setcanceltype(PTHREAD_CANCEL_DISABLE,NULL);  // 设置取消方式为立即取消。int sockfd=(int)(long)arg;  // 与客户端的socket连接。int ibuflen=0;char strbuffer[1024];  // 存放数据的缓冲区。while (true){memset(strbuffer,0,sizeof(strbuffer));if (TcpRead(sockfd,strbuffer,&ibuflen,300)==false) break; // 接收客户端发过来的请求报文。logfile.Write("接收:%s\n",strbuffer);connection *conn=getconn();  // 获取一个数据库连接。// 处理业务sleep(2);freeconn(conn);  // 释放一个数据库连接。strcat(strbuffer,"ok");      // 在客户端的报文后加上"ok"。logfile.Write("发送:%s\n",strbuffer);if (TcpWrite(sockfd,strbuffer)==false) break;     // 向客户端回应报文。}logfile.Write("客户端已断开。\n");    // 程序直接退出,析构函数会释放资源。pthread_cleanup_pop(1);pthread_exit(0);}// 信号2和15的处理函数。void mainexit(int sig){logfile.Write("mainexit begin.\n");// 关闭监听的socket。TcpServer.CloseListen();// 取消全部的线程。for (int ii=0;ii<vpthid.size();ii++){logfile.Write("cancel %ld\n",vpthid[ii]);pthread_cancel(vpthid[ii]);}// 释放数据库连接池。freeconns();logfile.Write("mainexit end.\n");exit(0);
}// 线程清理函数。void pthmainexit(void *arg){logfile.Write("pthmainexit begin.\n");// 关闭与客户端的socket。close((int)(long)arg);// 从vpthid中删除本线程的id。for (int ii=0;ii<vpthid.size();ii++){if (vpthid[ii]==pthread_self()){vpthid.erase(vpthid.begin()+ii);}}logfile.Write("pthmainexit end.\n");
}// 初始化数据库连接池。bool initconns(){for (int ii=0;ii<10;ii++){if (conns[ii].connecttodb("scott/tiger","Simplified Chinese_China.ZHS16GBK") != 0){logfile.Write("connect database failed.\n%s\n",conns[ii].m_cda.message); return false;}}for (int ii=0;ii<10;ii++) pthread_mutex_init(&mutexs[ii],NULL);return true;}// 从连接池中获取一个数据库连接。connection *getconn(){for (int ii=0;ii<10;ii++){if (pthread_mutex_trylock(&mutexs[ii])==0) {//锁住了就说明数据库获取成功logfile.Write("get a conn[%d] ok.\n",ii);return &conns[ii];}}return NULL;}// 释放数据库连接。void freeconn(connection *in_conn){for (int ii=0;ii<10;ii++){if (in_conn==&conns[ii]) pthread_mutex_unlock(&mutexs[ii]);}}// 释放数据库连接池。void freeconns(){for (int ii=0;ii<10;ii++){conns[ii].disconnect(); pthread_mutex_destroy(&mutexs[ii]);}}

客户端

#include "../_freecplus.h"int main(int argc,char *argv[]){printf("pid=%d\n",getpid());CTcpClient TcpClient;   // 创建客户端的对象。if (TcpClient.ConnectToServer("172.21.0.3",5858)==false) // 向服务端发起连接请求。{printf("TcpClient.ConnectToServer(\"172.21.0.3\",5858) failed.\n"); return -1;}char strbuffer[1024];    // 存放数据的缓冲区。for (int ii=0;ii<50;ii++)   // 利用循环,与服务端进行5次交互。{memset(strbuffer,0,sizeof(strbuffer));snprintf(strbuffer,50,"(%d)这是第%d个超级女生,编号%03d。",getpid(),ii+1,ii+1);printf("发送:%s\n",strbuffer);if (TcpClient.Write(strbuffer)==false) break;    // 向服务端发送请求报文。memset(strbuffer,0,sizeof(strbuffer));if (TcpClient.Read(strbuffer,20)==false) break;  // 接收服务端的回应报文。       printf("接收:%s\n",strbuffer);sleep(5);}// 程序直接退出,析构函数会释放资源。
}

二、条件变量

互斥锁一个明显的缺点是只有两种状态:锁定和非锁定。
而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,他常和互斥锁一起使用,以免出现竞争条件。当条件不满足时,线程往往解开相应的互斥锁并阻塞线程然后等待条件发生变化。一旦其他的某个线程改变了条件变量,他将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。总的来说互斥锁是线程间互斥的机制,条件变量则是同步机制。

相关接口:
1、初始化条件变量

int pthread_cond_init(pthread_cond_t* cond,pthread_condattr_t* cond_attr);

参数2:条件变量属性,通常为默认值,传NULL即可

也可以使用静态初始化的方法,初始化条件变量:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

2、阻塞等待一个条件变量

int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);

函数作用:
1、释放互斥锁。
2、等待条件。
3、条件被触发。
4、给互斥锁加锁
3、4步为原子操作

3、限时等待一个条件变量

int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime);

参3: 参看man sem_timedwait函数,查看struct timespec结构体。
struct timespec {
time_t tv_sec; /* seconds / 秒
long tv_nsec; /
nanosecondes*/ 纳秒
}

形参abstime:绝对时间。
如:time(NULL)返回的就是绝对时间。而alarm(1)是相对时间,相对当前时间定时1秒钟。
struct timespec t = {1, 0};
pthread_cond_timedwait (&cond, &mutex, &t); 只能定时到 1970年1月1日 00:00:01秒(早已经过去)

正确用法:

time_t cur = time(NULL); 获取当前时间。

struct timespec t; 定义timespec 结构体变量t

t.tv_sec = cur+1; 定时1秒

pthread_cond_timedwait (&cond, &mutex, &t);

4、唤醒至少一个阻塞在条件变量上的线程

int pthread_cond_signal(pthread_cond_t *cond);

5、唤醒全部阻塞在条件变量上的线程

int pthread_cond_broadcast(pthread_cond_t *cond);

6、销毁一个条件变量

int pthread_cond_destroy(pthread_cond_t *cond);

1、条件变量与互斥锁结合

#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<signal.h>
using namespace std;//声明互斥锁和条件变量pthread_mutex_t mutex;pthread_cond_t cond;void *thread1(void* arg){while(1){pthread_mutex_lock(&mutex);cout<<"线程一开始等待条件"<<endl;pthread_cond_wait(&cond,&mutex);cout<<"线程一被唤醒"<<endl;pthread_mutex_unlock(&mutex);}
}void *thread2(void* arg){while(1){pthread_mutex_lock(&mutex);cout<<"线程二开始等待条件"<<endl;pthread_cond_wait(&cond,&mutex);cout<<"线程二被唤醒"<<endl;pthread_mutex_unlock(&mutex);}
}void func(int sig){pthread_cond_signal(&cond);//pthread_cond_broadcast(&cond);//广播
}
int main()
{signal(15,func);//初始化锁pthread_mutex_init(&mutex,NULL);//NULL表示当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁//初始化条件变量pthread_cond_init(&cond,NULL);//创建两个线程pthread_t pthid1,pthid2;pthread_create(&pthid1,NULL,thread1,NULL);pthread_create(&pthid2,NULL,thread2,NULL);//等待线程退出pthread_join(pthid1,NULL);pthread_join(pthid2,NULL);//销毁锁pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}

2、条件变量与互斥锁实现(生产-消费者模型)高速缓存


1、

// 本程序演示用互斥锁和条件变量实现高速缓存。#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <vector>using namespace std;int mesgid=1;// 消息的记数器。// 缓存消息的结构体。
struct st_message{int mesgid;char message[1024];
} stmesg;vector<struct st_message> vcache; // 用vector容器做缓存,缓存为共享区域。pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 声名并初始化条件变量。
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 声名并初始化互斥锁。// 消费者、出队线程主函数。
void *outcache(void *arg){struct st_message stmesg;while (true){pthread_mutex_lock(&mutex); // 访问共享区域必须加锁加锁。// 如果缓存为空,等待。 // 条件变量虚假唤醒。while (vcache.size() == 0){pthread_cond_wait(&cond,&mutex);//三个消费者全部阻塞在这里,当被唤醒时,跑的快的线程立马锁住进而去操作}// 从缓存中获取第一条记录,然后删除该记录。memcpy(&stmesg,&vcache[0],sizeof(struct st_message)); // 内存拷贝。vcache.erase(vcache.begin());pthread_mutex_unlock(&mutex); // 解锁。// 以下是处理业务的代码。printf("phid=%ld,mesgid=%d\n",pthread_self(),stmesg.mesgid);usleep(100);//因为互斥锁存在优先唤醒的问题,加上这行代码是为了模拟实际业务运行,防止业务被一个线程一直执行,其他线程得不到机会}
}// 生产者、把生产的数据存入缓存。
void incache(int sig){struct st_message stmesg;memset(&stmesg,0,sizeof(struct st_message));pthread_mutex_lock(&mutex); // 访问共享区域必须加锁。// 生产数据,放入缓存。stmesg.mesgid=mesgid++; vcache.push_back(stmesg);// 内存拷贝。缓存可用链表避免//stmesg.mesgid=mesgid++; vcache.push_back(stmesg);//stmesg.mesgid=mesgid++; vcache.push_back(stmesg);//stmesg.mesgid=mesgid++; vcache.push_back(stmesg);//stmesg.mesgid=mesgid++; vcache.push_back(stmesg);pthread_mutex_unlock(&mutex); // 解锁。pthread_cond_broadcast(&cond); // 触发条件,激活全部的线程。//pthread_cond_signal(&cond);//唤醒一个等待条件的线程。
}int main(){signal(15,incache); // 接收15的信号,调用生产者函数。pthread_t thid1,thid2,thid3;pthread_create(&thid1,NULL,outcache,NULL);pthread_create(&thid2,NULL,outcache,NULL);pthread_create(&thid3,NULL,outcache,NULL);pthread_join(thid1,NULL);pthread_join(thid2,NULL);pthread_join(thid3,NULL);return 0;
}

运行结果:

[root@localhost mutex]# g++ -o cache cache.cpp -lpthread
[root@localhost mutex]# ./cache
phid=140295864997632,mesgid=1
phid=140295856604928,mesgid=2
phid=140295848212224,mesgid=3
phid=140295856604928,mesgid=4
phid=140295864997632,mesgid=5

2、

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>//节点结构体,链表作为共享数据(数据缓存),需被互斥量保护/
struct ListNode{int num; //数据区struct ListNode *next; //链表区
};struct ListNode *head = NULL;//头指针
struct ListNode *node = NULL;  //节点指针
//利用宏定义的方式初始化全局的互斥锁和条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;void *producter(void *arg){while (1) {//node =(struct ListNode*)malloc(sizeof(struct ListNode));//node->num = rand() % 400 + 1;//printf("---producted---%d\n", node->num);pthread_mutex_lock(&mutex);//访问共享区域必须加锁//node =(struct ListNode*)malloc(sizeof(struct ListNode));node->num = rand() % 400 + 1;printf("---producted---%d\n", node->num);node->next = head;head = node;//pthread_mutex_unlock(&mutex);pthread_cond_signal(&has_product);//通知消费者来消费sleep(rand() % 3);}return NULL;
}void *consumer(void *arg){while (1){pthread_mutex_lock(&mutex);//访问共享区域必须加锁///while (head == NULL){//如果共享区域没有数据,则解锁并等待条件变量pthread_cond_wait(&has_product, &mutex);}node = head;head = node->next;printf("------------------consumer--%d\n", mp->num);free(node); //释放被删除的节点内存node = NULL;//并将删除的节点指针指向NULL,防止野指针///pthread_mutex_unlock(&mutex);//printf("------------------consumer--%d\n", mp->num);//free(node); //释放被删除的节点内存//node = NULL;//并将删除的节点指针指向NULL,防止野指针sleep(rand() % 3);}return NULL;
}int main(void){pthread_t ptid, ctid;//创建生产者和消费者线程pthread_create(&ptid, NULL, producter, NULL);pthread_create(&ctid, NULL, consumer, NULL);//主线程回收两个子线程pthread_join(ptid, NULL);pthread_join(ctid, NULL);return 0;
}

运行结果:

[root@localhost mutex]# g++ -o cache1 cache1.cpp -lpthread
[root@localhost mutex]# ./cache1
---producted---184
------------------consumer--184
---producted---116
------------------consumer--116
---producted---187
---producted---250
------------------consumer--250
---producted---28
------------------consumer--28
---producted---164
------------------consumer--164
------------------consumer--187
---producted---373
------------------consumer--373
---producted---169
---producted---30
------------------consumer--30
---producted---63

条件变量的优点:
相较于mutex而言,条件变量可以减少竞争。
如直接使用mutex,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量,但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。

三、自旋锁

如果进线程无法取得锁,进线程不会立刻放弃CPU时间片,而是一直循环尝试获取锁,直到获取为止。

如果别的线程长时期占有锁,那么自旋就是在浪费CPU做无用功,但是自旋锁一般应用于加锁时间很短的场景,这个时候效率比较高。

自旋锁属于busy-waiting类型的锁,如果线程A是使用pthread_spin_lock操作去请求锁,如果自旋锁已经被线程B所持有,那么线程A就会一直在core 0上进行忙等待并不停的进行锁请求,检查该自旋锁是否已经被线程B释放,直到得到这个锁为止。

因为自旋锁不会引起调用者睡眠,所以自旋锁的效率远高于互斥锁。

虽然它的效率比互斥锁高,但是它也有些不足之处:
自旋锁一直占用CPU,在未获得锁的情况下,一直进行自旋,所以占用着CPU,如果不能在很短的时间内获得锁,无疑会使CPU效率降低。
在用自旋锁时有可能造成死锁,当递归调用时有可能造成死锁。

自旋锁只有在内核可抢占式或SMP的情况下才真正需要,在单CPU且不可抢占式的内核下,自旋锁的操作为空操作。自旋锁适用于锁使用者保持锁时间比较短的情况下。

相关API:

1、初始化锁

int pthread_spin_init(pthread_spinlock_t* lock,int pshared);

2、阻塞加锁

int pthread_spin_lock(pthread_spinlock_t* lock);

3、非阻塞加锁

int pthread_spin_trylock(pthread_spinlock_t* lock);

4、解锁

int pthread_spin_unlock(pthread_spinlock_t* lock);

5、销毁锁

int pthread_spin_destorya(pthread_spinlock_t* lock);
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<unistd.h>
#include<pthread.h>
#include<cstring>
using namespace std;char buffer[1024];//全局共享的bufferpthread_spinlock_t spin;//声明自旋锁void *pthfun(void *arg){for(int ii=0;ii<2;ii++){cout<<time(0)<<":"<<(long)arg<<">>lock..."<<endl;//第一步,加锁pthread_spin_lock(&spin);cout<<time(0)<<":"<<(long)arg<<">>lock ok! 正在处理数据..."<<endl;//第二步,操作共享的全局变量sprintf(buffer,"%d:%ld,%d",time(0),pthread_self(),ii);sleep(5);//第三步,解锁pthread_spin_unlock(&spin);cout<<time(0)<<":"<<(long)arg<<">>unlock...ok!"<<endl;usleep(100);}
}int main()
{   //初始化锁pthread_spin_init(&spin,PTHREAD_PROCESS_PRIVATE);//当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁//创建两个线程pthread_t pthid1,pthid2;pthread_create(&pthid1,NULL,pthfun,(void*)1);pthread_create(&pthid2,NULL,pthfun,(void*)2);//等待线程退出pthread_join(pthid1,NULL);pthread_join(pthid2,NULL);//销毁锁pthread_spin_destroy(&spin);return 0;
}

四、读写锁

多个读者可以同时进行读
写者必须互斥(只允许一个写者写,也不能读者写者同时进行)
写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)

相关API:

1、初始化读写锁

int pthread_rwlock_init(pthread);

2、申请读锁

int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);

3、申请写锁

int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);

4、尝试申请写锁,若有线程持有读锁或写锁,返回失败,否则成功

int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock);

5、解锁

int pthread_rwlock_unlock(pthread_rwlock_t* rwlock);

6、销毁读写锁

int pthread_rwlock_destroy(pthread_rwlock_t* rwlock);

互斥锁、条件变量、自旋锁、读写锁相关推荐

  1. 【线程、锁】什么是AQS(锁分类:自旋锁、共享锁、独占锁、读写锁)

    文章目录 1. 什么是AQS 1.1 锁分类 1.2 具体实现 2. AQS底层使用了模板方法模式 3. AQS的简单应用 参考 1. 什么是AQS AQS:全称为AbstractQuenedSync ...

  2. go Mutex (互斥锁)和RWMutex(读写锁)

    golang中sync包实现了两种锁Mutex (互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能. type Mutex func ...

  3. c语言用户态锁使用,用户态自旋锁、读写自旋锁及互斥锁

    1.自旋锁 自旋锁最多可能被一个可执行线程所持有.一个被征用的自旋锁使得请求它的线程在等待锁重新可用时自旋(特别浪费处理器时间).所以自旋锁不应该被长时间持有. 自旋锁是不可递归的! (1)自旋锁相关 ...

  4. 自旋锁和互斥锁实例_自旋锁和互斥锁的实现以及使用区别

    一.自旋锁和互斥锁的实现 基于硬件原语的一些抽象(比如:中断禁用.原子操作指令),怎么实现?可以参考清华大学操作公开课(向勇.陈渝老师讲的),以下摘抄一部分实现代码来实现抽象. Test And Se ...

  5. 信号量 互斥量 条件变量

    原文:https://blog.csdn.net/qq_32646795/article/details/78221005 本文打算写一些和锁有关的东西,谈一谈我对锁的原理和实现的理解,主要包含以下方 ...

  6. 锁9---自旋锁 VS 适应性自旋锁

    锁9-自旋锁 VS 适应性自旋锁 ************ 如有侵权请提示删除 *************** 文章目录 锁9---自旋锁 VS 适应性自旋锁 自旋锁 1.概念: 2.提出背景 3.自 ...

  7. 锁机制(自旋锁-乐观锁-悲观锁)

    各种锁机制(主要介绍自旋锁) 1. 自旋锁 2. 悲观锁---Synchornized 2.1 偏向锁 2.1.1 为什么要引入偏向锁? 2.1.2 偏向锁原理和升级过程 2.2 轻量级锁 2.2.1 ...

  8. java -锁(公平、非公平锁、可重入锁【递归锁】、自旋锁)

    1.公平锁.非公平锁 2.可重入锁(递归锁) 3.自旋锁 AtomicReference atomicReference = new AtomicReference();//原子引用线程 下面代码5秒 ...

  9. JUC并发编程系列详解篇十四(自旋锁 VS 适应性自旋锁)

    自旋锁 由于在多处理器环境中某些资源的有限性,有时需要互斥访问(mutual exclusion),这时候就需要引入锁的概念,只有获取了锁的线程才能够对资源进行访问,由于多线程的核心是CPU的时间分片 ...

  10. 偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化

    JDK1.6 对锁的优化: 偏向锁.轻量级锁.自旋锁.适应性自旋锁.锁消除.锁粗化 等技术. 锁主要存在四中状态,依次是: 无锁状态 偏向锁状态 轻量级锁状态 重量级锁状态 锁可以升级不可降级,即 无 ...

最新文章

  1. Consultanting Service
  2. pandas删除数据行中的重复数据行、基于dataframe所有列删除重复行、基于特定数据列或者列的作何删除重复行、删除重复行并保留重复行中的最后一行、pandas删除所有重复行(不进行数据保留)
  3. python基础教程书籍推荐-小猿圈推荐Python入门书籍,不知道这些你就太low了。
  4. SaCa DataViz 企业版 | 高性能大数据分析引擎
  5. win10照片查看器_图片打开方式中找不到Windows图片查看器怎么办
  6. Linux 命令(126)—— ssh 命令
  7. 聊聊 归一化和标准化
  8. 【Python】AxisError: axis 0 is out of bounds for array of dimension 0
  9. 解决问题:UserWarning: Matplotlib is currently using agg, which is a non-GUI backend.
  10. asp.net 导出word文档
  11. C语言定义定长整型数组,C语言变长讯息定义:柔性数组
  12. 5个必看的Docker视频
  13. newton插值公式
  14. html打印26个字母,用js完成:输出26个英文字母任意3个字母的全部组合
  15. 初识SecureCRT工具
  16. httpclient请求webservice接口
  17. 光伏辐照和发电功率数据集
  18. 使用turtle绘制心心相印(动态)python
  19. [PTA]习题11-1 输出月份英文名
  20. AI Earth ——开发者模式案例7:植被覆盖度提取

热门文章

  1. 初识canvas,使用canvas做一个百分比加载进度的动画
  2. 个人阅读 代码大全的阅读与提问
  3. 此时无法停用连接。这个连接可能在用一个或多个不支持即插即用的协议,或者它是由其他用户或系统帐户初始化的。...
  4. js入门系列演示·数组
  5. java 是否继续操作?代码
  6. 【python数据挖掘课程】二十九.数据预处理之字符型转换数值型、标准化、归一化处理
  7. Git之变基方式Rebase的使用
  8. SwiftUI之深入解析如何创建列表展示视图和列表如何导航跳转新页面
  9. Python之打造专属Python开发者的完美终端工具Rich
  10. 【数据结构与算法】之给Nx3网格图涂色的方案数的求解算法