互斥锁、条件变量、自旋锁、读写锁

一、互斥锁

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

互斥锁是在抢锁失败的情况下主动放弃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
#include
#include
#include
#include
#include
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)
互斥锁、条件变量、自旋锁、读写锁_第1张图片

/*
 *  编译指令: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和15

  logfile.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
#include
#include
#include
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、条件变量与互斥锁实现(生产-消费者模型)高速缓存
互斥锁、条件变量、自旋锁、读写锁_第2张图片
互斥锁、条件变量、自旋锁、读写锁_第3张图片
1、

// 本程序演示用互斥锁和条件变量实现高速缓存。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

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 
#include 
#include 
#include 

//节点结构体,链表作为共享数据(数据缓存),需被互斥量保护/
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
#include
#include
#include
#include
#include
using namespace std;

char buffer[1024];//全局共享的buffer

pthread_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);

你可能感兴趣的:(linux操作系统,linux,锁)